diff --git a/.fvm/flutter_sdk b/.fvm/flutter_sdk new file mode 120000 index 000000000..10d6e348b --- /dev/null +++ b/.fvm/flutter_sdk @@ -0,0 +1 @@ +/Users/broadli/fvm/3.3.0 \ No newline at end of file diff --git a/.fvm/fvm_config.json b/.fvm/fvm_config.json new file mode 100644 index 000000000..e44645b53 --- /dev/null +++ b/.fvm/fvm_config.json @@ -0,0 +1,4 @@ +{ + "flutterSdkVersion": "3.3.0", + "flavors": {} +} \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug-report.zh-CN.yml b/.github/ISSUE_TEMPLATE/bug-report.zh-CN.yml index e30a7bba4..8a87371a7 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.zh-CN.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.zh-CN.yml @@ -6,11 +6,13 @@ body: attributes: value: | # 欢迎你的参与 - tdesign-flutter 的 Issue 列表接受 bug 报告或是新功能请求。也可加入官方社区: + tdesign-flutter 的 Issue 列表接受 bug 报告或是新功能请求。也可加入官方社区: + + 在发布一个 Issue 前,请确保: - - 在 [常见问题](https://tdesign.tencent.com/about/faq)、[更新日志](https://github.com/TDesignOteam/tdesign-flutter/blob/main/CHANGELOG.md) 和 [旧Issue列表](https://github.com/TDesignOteam/tdesign-flutter/issues?q=is%3Aissue) 中搜索过你的问题。(你的问题可能已有人提出,也可能已在最新版本中被修正) - - 如果你发现一个已经关闭的旧 Issue 在最新版本中仍然存在,不要在旧 Issue 下面留言,请建一个新的 issue。 + - 在 [常见问题](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-site/FAQ.md)、[更新日志](https://tdesign.tencent.com/flutter/changelog) 和 [Issue列表](https://github.com/Tencent/tdesign-flutter/issues) 中搜索过你的问题。(你的问题可能已有人提出,也可能已在最新版本中被修正) + - 如果你发现一个已经关闭的旧 Issue 在最新版本中仍然存在,不要在旧 Issue 下面留言,请建一个新的 issue。 - type: input id: version @@ -31,7 +33,7 @@ body: - type: textarea id: reproduceSteps attributes: - label: 重现步骤 + label: 重现步骤/代码 description: 请清晰的描述重现该 Issue 的步骤,这能帮助我们快速定位问题。没有清晰重现步骤将不会被修复,标有 'need reproduction' 的 Issue 在 7 天内不提供相关步骤,将被关闭。 placeholder: 请填写 diff --git a/.github/ISSUE_TEMPLATE/doc-report.zh-CN.yml b/.github/ISSUE_TEMPLATE/doc-report.zh-CN.yml new file mode 100644 index 000000000..24ee02e3e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/doc-report.zh-CN.yml @@ -0,0 +1,23 @@ +name: 反馈文档 +description: 通过 github 模板进行文档反馈。 +title: "[文档名称] 描述问题的标题" +body: + - type: markdown + attributes: + value: | + # 欢迎你的参与 + tdesign-flutter 的 Issue 列表接受 bug 报告或是新功能请求。也可加入官方社区: + + + + 在发布一个 Issue 前,请确保: + - 在 [常见问题](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-site/FAQ.md)、[更新日志](https://tdesign.tencent.com/flutter/changelog) 和 [Issue列表](https://github.com/Tencent/tdesign-flutter/issues) 中搜索过你的问题。(你的问题可能已有人提出,也可能已在最新版本中被修正) + - 如果你发现一个已经关闭的旧 Issue 在最新版本中仍然存在,不要在旧 Issue 下面留言,请建一个新的 issue。 + + - type: textarea + id: docModificationPlan + attributes: + label: 你建议的文案是什么 + placeholder: 请填写 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature-report.zh-CN.yml b/.github/ISSUE_TEMPLATE/feature-report.zh-CN.yml index 3d23ab0a1..8c3eed530 100644 --- a/.github/ISSUE_TEMPLATE/feature-report.zh-CN.yml +++ b/.github/ISSUE_TEMPLATE/feature-report.zh-CN.yml @@ -6,11 +6,13 @@ body: attributes: value: | # 欢迎你的参与 - tdesign-flutter 的 Issue 列表接受 bug 报告或是新功能请求。也可加入官方社区: - + tdesign-flutter 的 Issue 列表接受 bug 报告或是新功能请求。也可加入官方社区: + + + 在发布一个 Issue 前,请确保: - - 在 [常见问题](https://tdesign.tencent.com/about/faq)、[更新日志](https://github.com/TDesignOteam/tdesign-flutter/blob/main/CHANGELOG.md) 和 [旧Issue列表](https://github.com/TDesignOteam/tdesign-flutter/issues?q=is%3Aissue) 中搜索过你的问题。(你的问题可能已有人提出,也可能已在最新版本中被修正) - - 如果你发现一个已经关闭的旧 Issue 在最新版本中仍然存在,不要在旧 Issue 下面留言,请建一个新的 issue。 + - 在 [常见问题](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-site/FAQ.md)、[更新日志](https://tdesign.tencent.com/flutter/changelog) 和 [Issue列表](https://github.com/Tencent/tdesign-flutter/issues) 中搜索过你的问题。(你的问题可能已有人提出,也可能已在最新版本中被修正) + - 如果你发现一个已经关闭的旧 Issue 在最新版本中仍然存在,不要在旧 Issue 下面留言,请建一个新的 issue。 - type: textarea id: functionContent diff --git a/.github/ISSUE_TEMPLATE/new-component.zh-CN.md b/.github/ISSUE_TEMPLATE/new-component.zh-CN.md index 3408d6b90..25095c74c 100644 --- a/.github/ISSUE_TEMPLATE/new-component.zh-CN.md +++ b/.github/ISSUE_TEMPLATE/new-component.zh-CN.md @@ -13,18 +13,22 @@ issue label 需要设置为 help wanted,发起招募 --> ## 组件名称中英文 - + + ### 设计稿 设计稿: +截图: + ### 参与贡献 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d7e832812..8e2b679f0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,8 @@ ### 🤔 这个 PR 的性质是? +> 勾选规则: +> 1.只要有新增参数,就勾选”新特性提交“ +> 2.只修改内部bug,未新增参数,才勾选”日常 bug 修复“ +> 3.其他选项视具体改动判断 - [ ] 日常 bug 修复 - [ ] 新特性提交 @@ -40,7 +44,7 @@ ⚠️ 请自检并全部**勾选全部选项**。⚠️ -- [ ] 文档已补充或无须补充 -- [ ] 代码演示已提供或无须提供 -- [ ] TypeScript 定义已补充或无须补充 -- [ ] Changelog 已提供或无须提供 +- [ ] pr目标分支为develop分支,请勿直接往main分支合并 +- [ ] 标题格式为:`组件类名`: 修改描述(示例:`TDBottomTabBar`: 修复iconText模式,底部溢出2.5像素) +- [ ] ”相关issue“处带上修复的issue链接 +- [ ] 相关文档已补充或无须补充 diff --git a/.github/_workflows/issue-stale-close.temp.yml b/.github/_workflows/issue-stale-close.temp.yml new file mode 100644 index 000000000..c8f8111ef --- /dev/null +++ b/.github/_workflows/issue-stale-close.temp.yml @@ -0,0 +1,28 @@ +## 忽略当前 ci 功能,待修复 +## force copy from tencent/tdesign +## 国际标准时间+8 +#name: Close stale issues and PRs +#on: +# schedule: +# - cron: "50 5 * * *" +# +#jobs: +# close-issues: +# runs-on: ubuntu-latest +# permissions: +# issues: write +# pull-requests: write +# steps: +# - uses: actions/stale@v4 +# with: +# stale-issue-message: "这个 Issue 被标记为了过时 stale ,因为它已经持续 30 天没有任何活动了。删除 stale 标签或评论,否则将在 7 天内关闭。" +# stale-pr-message: '这个 PR 已经过时了,因为它已经持续 45 天没有任何活动了。 删除 stale 的标签或评论,否则将在 10 天内关闭。' +# close-issue-message: "此 Issue 被自动关闭,因为它自被标记为过时 stale 以来已闲置 7 天。" +# close-pr-message: "此 PR 被自动关闭,因为它已经 stable 停滞了 10 天,没有任何活动。" +# days-before-stale: 30 +# days-before-close: 7 +# days-before-pr-stale: 45 +# days-before-pr-close: 10 +# repo-token: ${{ secrets.GITHUB_TOKEN }} +# exempt-issue-labels: 'WIP' +# exempt-pr-labels: 'WIP' \ No newline at end of file diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml new file mode 100644 index 000000000..1ed575905 --- /dev/null +++ b/.github/workflows/auto-release.yml @@ -0,0 +1,98 @@ +name: Auto Release + +on: + pull_request: + branches: [develop] + types: [opened, synchronize, reopened, closed] + paths: + - "tdesign-component/pubspec.yaml" + issue_comment: + types: [edited] + +jobs: + generator: + runs-on: ubuntu-latest + if: > + github.event_name == 'pull_request' && + github.event.pull_request.merged == false && + startsWith(github.head_ref, 'release/') + steps: + - run: echo "The head of this PR starts with 'release/'" + - uses: actions/checkout@v4 + - uses: TDesignOteam/tdesign-tag-action@main + id: tag-action + with: + token: ${{ secrets.GITHUB_TOKEN }} + version_file: ./tdesign-component/pubspec.yaml + - uses: TDesignOteam/tdesign-changelog-action@main + id: changelog + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag: ${{ steps.tag-action.outputs.version }} + - name: Add comment + uses: peter-evans/create-or-update-comment@v1 + with: + issue-number: ${{ github.event.pull_request.number }} + body: | + ${{ steps.changelog.outputs.changelog }} + comment_add_log: + runs-on: ubuntu-latest + if: > + github.event_name == 'issue_comment' + && github.event.issue.pull_request + && github.event.sender.login == github.event.issue.user.login + && startsWith(github.event.comment.body, '## 🌈 ') + steps: + - id: comment + shell: bash + run: | + result=$(curl ${{github.event.issue.pull_request.url}} -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}") + headrefreg='"ref": "(release/[[:digit:]]{1,2}\.[[:digit:]]{1,2}\.[[:digit:]]{1,2})",' + if [[ $result =~ $headrefreg ]] + then + echo "属于 release pr 的 comment ${BASH_REMATCH[1]}" + else + echo "不属于 release pr 的 comment" && exit 1 + fi + echo "::set-output name=branch::${BASH_REMATCH[1]}" + # zsh $match[1] + - uses: actions/checkout@v3 + with: + ref: ${{ steps.comment.outputs.branch }} + - name: Commit and push if needed + env: + BODY: ${{ github.event.comment.body }} + run: | + txt=$(cat tdesign-site/CHANGELOG.md) + echo "${txt%%##*}$BODY${txt##*---}" > tdesign-site/CHANGELOG.md + git add . + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git commit -m "chore: changelog's changes" + git push + echo "💾 pushed changelog's changes" + merge_tag: + runs-on: ubuntu-latest + if: > + github.event_name == 'pull_request' && + github.event.pull_request.merged == true && + startsWith(github.head_ref, 'release/') + steps: + - uses: actions/checkout@v4 + with: + ref: develop + token: ${{ secrets.PERSONAL_TOKEN }} + - uses: TDesignOteam/tdesign-tag-action@main + id: tag-action + with: + token: ${{ secrets.GITHUB_TOKEN }} + version_file: ./tdesign-component/pubspec.yaml + - name: tag and push if needed + run: | + echo "${{ steps.tag-action.outputs.version }}" + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git tag ${{ steps.tag-action.outputs.version }} + git push origin ${{ steps.tag-action.outputs.version }} + echo "pushed tag ${{ steps.tag-action.outputs.version }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..19e6fb48b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,119 @@ +name: ci + +on: + pull_request: + branches: [develop, main] + types: [opened, synchronize, reopened] + +jobs: + build-apk: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + flutter-version: + - 3.16.9 + - 3.x + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ matrix.flutter-version }} + cache: true + + - run: flutter --version + + - name: init.sh + working-directory: ./tdesign-component + run: | + chmod +x ./init.sh + ./init.sh + + - name: flutter pub get + working-directory: ./tdesign-component + run: flutter pub get + + - name: flutter pub get + working-directory: ./tdesign-component/example + run: flutter pub get + + - name: flutter build + working-directory: ./tdesign-component/example + run: flutter build apk -t ./lib/main.dart --release + + + build-ios: + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + flutter-version: + - 3.16.9 + - 3.x + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ matrix.flutter-version }} + cache: true + + - run: flutter --version + + - name: init.sh + working-directory: ./tdesign-component + run: | + chmod +x ./init.sh + ./init.sh + + - name: flutter pub get + working-directory: ./tdesign-component + run: flutter pub get + + - name: flutter pub get + working-directory: ./tdesign-component/example + run: flutter pub get + + - name: flutter build + working-directory: ./tdesign-component/example + run: flutter build ios --release --no-codesign + + build-web: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + flutter-version: + - 3.16.9 + - 3.x + steps: + - uses: actions/checkout@v4 + + - uses: subosito/flutter-action@v2 + with: + channel: "stable" + flutter-version: ${{ matrix.flutter-version }} + cache: true + + - run: flutter --version + + - name: init.sh + working-directory: ./tdesign-component + run: | + chmod +x ./init.sh + ./init.sh + + - name: flutter pub get + working-directory: ./tdesign-component + run: flutter pub get + + - name: flutter pub get + working-directory: ./tdesign-component/example + run: flutter pub get + + - name: flutter build + working-directory: ./tdesign-component/example + run: flutter build web diff --git a/.github/workflows/issue-stale-close.temp.yml b/.github/workflows/issue-stale-close.temp.yml deleted file mode 100644 index 14e04f4b5..000000000 --- a/.github/workflows/issue-stale-close.temp.yml +++ /dev/null @@ -1,27 +0,0 @@ -# force copy from tencent/tdesign -# 国际标准时间+8 -name: Close stale issues and PRs -on: - schedule: - - cron: "50 5 * * *" - -jobs: - close-issues: - runs-on: ubuntu-latest - permissions: - issues: write - pull-requests: write - steps: - - uses: actions/stale@v4 - with: - stale-issue-message: "这个 Issue 被标记为了过时 stale ,因为它已经持续 30 天没有任何活动了。删除 stale 标签或评论,否则将在 7 天内关闭。" - stale-pr-message: '这个 PR 已经过时了,因为它已经持续 45 天没有任何活动了。 删除 stale 的标签或评论,否则将在 10 天内关闭。' - close-issue-message: "此 Issue 被自动关闭,因为它自被标记为过时 stale 以来已闲置 7 天。" - close-pr-message: "此 PR 被自动关闭,因为它已经 stable 停滞了 10 天,没有任何活动。" - days-before-stale: 30 - days-before-close: 7 - days-before-pr-stale: 45 - days-before-pr-close: 10 - repo-token: ${{ secrets.GITHUB_TOKEN }} - exempt-issue-labels: 'WIP' - exempt-pr-labels: 'WIP' \ No newline at end of file diff --git a/.github/workflows/pr-spelling.template.yml b/.github/workflows/pr-spelling.template.yml new file mode 100644 index 000000000..d6237cdd1 --- /dev/null +++ b/.github/workflows/pr-spelling.template.yml @@ -0,0 +1,13 @@ +name: pr-spell-check +on: [pull_request] + +jobs: + run: + name: Spell Check with Typos + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check spelling + uses: crate-ci/typos@master + with: + config: .github/workflows/typos-config.toml diff --git a/.github/workflows/tag-push.yml b/.github/workflows/tag-push.yml new file mode 100644 index 000000000..064d55e58 --- /dev/null +++ b/.github/workflows/tag-push.yml @@ -0,0 +1,24 @@ +# 文件名建议统一为 tag-push.yml +# 应用 publish.yml 的 demo + +name: TAG_PUSH + +on: create + +jobs: + TAG_PUSH: + runs-on: ubuntu-latest + if: github.event.ref_type == 'tag' + steps: + - uses: actions/checkout@v4 + with: + ref: main + fetch-depth: 0 + token: ${{ secrets.PERSONAL_TOKEN }} + - run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git status + git fetch origin + git merge origin/develop + git push origin main diff --git a/.github/workflows/typos-config.toml b/.github/workflows/typos-config.toml new file mode 100644 index 000000000..d9918c1e2 --- /dev/null +++ b/.github/workflows/typos-config.toml @@ -0,0 +1,6 @@ +default.check-filename = true + +[default.extend-words] + +[files] +extend-exclude = ["project.pbxproj","aop_flutter_sdk.patch"] diff --git a/.gitignore b/.gitignore index d7fe93933..9a90b4cb2 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ .history .svn/ +tdesign-site/node_modules/ + # IntelliJ related *.iml *.ipr @@ -71,4 +73,8 @@ build/ !**/ios/**/default.mode1v3 !**/ios/**/default.mode2v3 !**/ios/**/default.pbxuser -!**/ios/**/default.perspectivev3 \ No newline at end of file +!**/ios/**/default.perspectivev3 +/.fvm/ +/tdesign-component/local_dependency_override.yaml +/tdesign-component/pubspec_overrides.yaml +/tdesign-component/example/pubspec_overrides.yaml diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..f93c480d0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "tdesign-site/src/_common"] + path = tdesign-site/src/_common + url = https://github.com/Tencent/tdesign-common.git + branch = develop diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..f95d69073 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..0ea942569 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,27 @@ +# CONTRIBUTING + +## 目录结构 + +```text +tdesign-component/ +├── demo_tool // API和演示代码 +├── examples // 组件使用示例 +├── lib // 组件库 +└── tests // 组件测试 + +tdesign-site/ // tdesign flutter站点 +``` + +## 开发规范 + +- 组件命名规范:以TD为前缀,组件名称、API名称参考TDesign现有组件和API命名,可以根据flutter原生Widget的特点进行修改。组件API以满足设计要求和使用为准,可根据flutter特点做精简或定制。 +- 组件库用到的所有色值、圆角、字体字号等样式属性需全部定义在主题中。 +- 代码规范遵循腾讯Dart代码规范。 +- 对于系统原有组件,如Text,Image等,应兼容系统原组件功能,只能扩展,不能阉割,以免业务需要使用系统功能时,必须放弃TDesign控件。 +- 示例页面尽量使用ExamplePage+ExampleModule+ExampleItem组合,按照示例稿的布局实现;页面写完后,在main.dart中修改exampleMap对应组件的isTodo属性即可。 +- 组件API和演示代码,请参考demo_tool/README.md文件。 +- 组件内部的固定文案,都应该抽离到TDResourceDelegate中统一管理,方便业务进行国际化适配 +- 如果使用的组件TD有封装,尽量使用TD已有组件,而非直接使用系统组件 + +## 更多 +更多内容请参考:[贡献指南](https://tdesign.tencent.com/flutter/develop) \ No newline at end of file diff --git a/LICENSE b/LICENSE index 789cbde93..018620472 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,9 @@ MIT License -Copyright (c) 2021-present TDesign +Copyright (c) 2023-present TDesign, tdesign@tencent.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/PUBLISH.md b/PUBLISH.md new file mode 100644 index 000000000..ca6de13e0 --- /dev/null +++ b/PUBLISH.md @@ -0,0 +1,10 @@ +# 版本发布流程 + +## 发布流程 + +- 从 `develop` 新建 `release/x.y.z` 分支,并修改 `tdesign-component/pubspec.yaml` 中的`version`版本号,推送分支至远程仓库,并提交一个合入`develop`的 Pull Request 到仓库 +- 仓库的 Github Action 会自动整理上个版本至今 commit 对应的 CHANGELOG,并将 CHANGELOG 的 draft 作为一个评论推送到该 Pull Request 上 +- 发布人检查 CHANGELOG,并优化内容逻辑结构,确认无误后删除对于评论首行提示,Github Action 会将优化后的内容写入 CHANGELOG.md 内 +- 确认无误后,合并分支入`develop` +- 合入 `develop` 后,仓库会触发 Github Action 合入`main`分支,并将版本号作为 `tag` 打在仓库上,并触发 Github Action 执行版本发布流程 +- 合入 `main` 分支后,站点的部署流水线 web hook 会监听到 `main` 分支的新增 commit,并触发流水线,官网更新站点 diff --git a/README.md b/README.md index 6d14a6801..421c197df 100644 --- a/README.md +++ b/README.md @@ -4,49 +4,103 @@

-TDesign Flutter技术栈组件库,适合在移动端项目中使用。 - -# 特性 - -- 提供TDesign设计风格的Flutter UI组件库 -- 支持根据App设计风格定制主题 -- 提供常用Icon库,支持定制替换 -- 根据TDesign规范定义颜色组,可在TDColors中查看,方便适配TDesign规范的组件 -- 色值声明类可以添加默认颜色,实时查看色值默认显示效果 - -# 使用方法 -- 在pubbspec.yaml引入依赖。 - - dependencies: - tdesign_flutter: - git: https://github.com/TDesignOteam/tdesign-flutter.git - -- 在文件头部引入:import 'package:tdesign_flutter/td_export.dart'; // 组件库相关的,只需要引入这个文件,里面暴露td前缀所有需要的类 -- 可通过json文件配置颜色/字体尺寸/字体样式/圆角/阴影等主题样式。通过TDTheme.of(context)或者TDTheme.defaultData()获取主题数据。建议组件都使用TDTheme.of(context)的,不需要跟随局部主题的组件,才可以使用TDTheme.defaultData()。 - - 颜色,字体,圆角等使用示例: - TDTheme.of(context).brandNormalColor - TDTheme.defaultData().fontM -- TDesign的Icon不跟随主题,都是ttf格式: - - 使用示例: - TDIcon(TDIcons.activity) - -- 使用示例:example/lib/tdesign/page - -# 开发规范 -- 组件命名规范:以TD为前缀,组件名称、API名称参考TDesign现有组件和API命名,可以根据flutter原生Widget的特点进行修改。组件API以满足设计要求和使用为准,可根据flutter特点做精简或定制。 -- 组件库用到的所有色值、圆角、字体字号等样式属性需全部定义在主题中。 -- 代码规范遵循腾讯Dart代码规范。 -- 对于系统原有组件,如Text,Image等,应兼容系统原组件功能,只能扩展,不能阉割,以免业务需要使用系统功能时,必须放弃TDesign控件。 -- 示例页面尽量使用ExampleWidget+ExampleItem组合,在ExampleItem的desc写明示例描述;页面写完后,在main.dart中将页面注册进examplePageList即可。 - - -# 其他技术栈实现 -- 桌面端 Vue 3 实现:[web-vue-next](https://github.com/Tencent/tdesign-vue-next) -- 桌面端 Vue 实现: [web-react](https://github.com/Tencent/tdesign-vue) -- 移动端小程序实现: [小程序](https://github.com/Tencent/tdesign-miniprogram) - -# 开源协议 - -TDesign 遵循 [MIT 协议](https://github.com/TDesignOteam/tdesign-flutter/blob/main/LICENSE) \ No newline at end of file +

+ + License + + + Version + + + Downloads + +

+ +English | [简体中文](README_zh_CN.md) + +Tencent TDesign UI component library of Flutter, suitable for use in mobile projects. + + +# 🎉 Features + +- Provides Flutter UI component library in TDesign design style +- Support customizing themes according to App design style +- Provides commonly used Icon library and supports customized replacement +- Define color groups according to the TDesign specification, which can be viewed in TDColors to facilitate the adaptation of components to the TDesign specification. +- The color value declaration class can add default colors and view the default display effect of color values in real time. + + +# 🔨 Usage +- SDK dependency version +```yaml +dart: ">=2.19.0 <4.0.0" +flutter: ">=3.7.0" +``` + +- Add the dependency in `pubspec.yaml` + +```yaml +dependencies: + tdesign_flutter: ^0.0.6 +``` + +- Import at the top of your file: + +```dart +import 'package:tdesign_flutter/tdesign_flutter.dart'; +``` + +- You can configure theme styles such as colors, font sizes, font styles, corner radius, and shadows through a json file. +Retrieve theme data using `TDTheme.of(context)` or `TDTheme.defaultData()`. It is recommended +that components use `TDTheme.of(context)` unless the component does not need to follow local themes, +in which case `TDTheme.defaultData()` can be used. + +Examples of using colors, fonts, and corner radius: + +```dart +TDTheme.of(context).brandNormalColor +TDTheme.defaultData().fontBodyLarge +``` + +- TDesign's icons do not follow the theme, they are all in ttf format, usage examples: + +```dart +Icon(TDIcons.activity) +``` + +- For more use examples, please refer to [example/lib/page](tdesign-component/example/lib/page) + + +# TDesign component libraries + +TDesign also provides component libraries for other platforms and frameworks. + +- component library for Vue 2.x : [tdesign-vue](https://github.com/Tencent/tdesign-vue) +- component library for Vue 3.x : [tdesign-vue-next](https://github.com/Tencent/tdesign-vue-next) +- component library for React : [tdesign-react](https://github.com/Tencent/tdesign-react) +- component library for Vue 3.x Mobile : [tdesign-mobile-vue](https://github.com/Tencent/tdesign-mobile-vue) +- component library for React Mobile : [tdesign-mobile-react](https://github.com/Tencent/tdesign-mobile-react) +- component library for Wechat miniprogram : [tdesign-miniprogram](https://github.com/Tencent/tdesign-miniprogram) + +# Acknowledgements + +TDesign Flutter depends on the following component libraries. We appreciate the authors for their open-source contributions: + +- [easy_refresh](https://pub-web.flutter-io.cn/packages/easy_refresh) +- [flutter_swiper](https://pub-web.flutter-io.cn/packages/flutter_swiper) +- [flutter_slidable](https://pub-web.flutter-io.cn/packages/flutter_slidable) +- [image_picker](https://pub-web.flutter-io.cn/packages/image_picker) + +# Contributing + +Contributing is welcome. Read [guidelines for contributing](CONTRIBUTING.md) before submitting your [Pull Request](https://github.com/Tencent/tdesign-flutter/pulls). + +# Feedback + +Create your [Github issues](https://github.com/Tencent/tdesign-flutter/issues) or scan the QR code below to join our user groups + + + +# License + +The MIT License. Please see [the license file](LICENSE) for more information. \ No newline at end of file diff --git a/README_zh_CN.md b/README_zh_CN.md new file mode 100644 index 000000000..4902a1136 --- /dev/null +++ b/README_zh_CN.md @@ -0,0 +1,102 @@ +

+ + TDesign Logo + +

+ +

+ + License + + + Version + + + Downloads + +

+ +简体中文 | [English](README.md) + +腾讯TDesign Flutter技术栈组件库,适合在移动端项目中使用。 + + +# 🎉 特性 + +- 提供 TDesign 设计风格的 Flutter UI 组件库。 +- 支持根据 App 设计风格定制主题。 +- 提供常用 Icon 库,支持定制替换。 +- 根据 TDesign 规范定义颜色组,可在 TDColors 中查看,方便适配 TDesign 规范的组件。 +- 色值声明类可以添加默认颜色,实时查看色值默认显示效果。 + + +# 🔨 使用 +- SDK 版本依赖 +```yaml +dart: ">=2.19.0 <4.0.0" +flutter: ">=3.7.0" +``` + +- 在 `pubspec.yaml` 添加依赖。 + +```yaml +dependencies: + tdesign_flutter: ^0.0.6 +``` + +- 在文件头部引入: + +```dart +import 'package:tdesign_flutter/tdesign_flutter.dart'; +``` + +- 您可以通过json文件配置主题样式,如颜色、字体大小、字体样式、角半径和阴影。使用 `TDTheme.of(context)` 或 `TDTheme.defaultData()` 获取主题数据。 + 建议组件使用 `TDTheme.of(context)`,除非组件不需要遵循本地主题,在这种情况下可以使用 `TDTheme.defaultData()`。 + +颜色,字体,圆角等使用示例: + +```dart +TDTheme.of(context).brandNormalColor +TDTheme.defaultData().fontBodyLarge +``` + +- TDesign的图标不遵循主题,它们都是ttf格式,使用示例: + +```dart +Icon(TDIcons.activity) +``` + +- 有关更多使用示例,请参阅 [example/lib/page](tdesign-component/example/lib/page) + + +# 其他技术栈实现 + +- 桌面端 Vue 2.X 版本实现:[tdesign-vue](https://github.com/Tencent/tdesign-vue) +- 桌面端 Vue 3.X 版本实现:[tdesign-vue-next](https://github.com/Tencent/tdesign-vue-next) +- 桌面端 React 实现:[tdesign-react](https://github.com/Tencent/tdesign-react) +- 手机端 Vue 3.X 版本实现:[tdesign-mobile-vue](https://github.com/Tencent/tdesign-mobile-vue) +- 手机端 React 实现:[tdesign-mobile-react](https://github.com/Tencent/tdesign-mobile-react) +- 移动端小程序实现:[tdesign-miniprogram](https://github.com/Tencent/tdesign-miniprogram) + +# 致谢 + +TDesign Flutter 依赖以下组件库,感谢作者的开源贡献: + +- [easy_refresh](https://pub-web.flutter-io.cn/packages/easy_refresh) +- [flutter_swiper](https://pub-web.flutter-io.cn/packages/flutter_swiper) +- [flutter_slidable](https://pub-web.flutter-io.cn/packages/flutter_slidable) +- [image_picker](https://pub-web.flutter-io.cn/packages/image_picker) + +# Contributing + +Contributing is welcome. Read [guidelines for contributing](CONTRIBUTING.md) before submitting your [Pull Request](https://github.com/Tencent/tdesign-flutter/pulls). + +# 反馈 + +有任何问题,建议通过 [Github issues](https://github.com/Tencent/tdesign-flutter/issues) 反馈或扫码加入用户微信群。 + + + +# 开源协议 + +TDesign 遵循 [MIT 协议](LICENSE)。 \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml deleted file mode 100644 index 660a4e245..000000000 --- a/analysis_options.yaml +++ /dev/null @@ -1,208 +0,0 @@ -analyzer: - enable-experiment: - - extension-methods - errors: - unused_import: ignore - camel_case_types: warning - missing_required_param: error - missing_return: warning - todo: ignore - non_constant_identifier_names: warning - constant_identifier_names: warning - library_names: warning - file_names: warning - avoid_setters_without_getters: warning - avoid_return_types_on_setters: warning - avoid_returning_null: warning - avoid_returning_this: warning - prefer_generic_function_type_aliases: warning - always_declare_return_types: warning - hash_and_equals: warning - avoid_equals_and_hash_code_on_mutable_classes: warning - avoid_null_checks_in_equality_operators: warning - prefer_interpolation_to_compose_strings: warning - prefer_collection_literals: warning - prefer_is_empty: warning - unnecessary_lambdas: warning - prefer_equal_for_default_values: warning - avoid_init_to_null: warning - -linter: - rules: - - avoid_unnecessary_containers - - no_logic_in_create_state - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_const_literals_to_create_immutables - - sized_box_for_whitespace - - use_full_hex_values_for_flutter_colors - - use_key_in_widget_constructors - # 1.11 使用大驼峰命名类型 - - camel_case_types - # 1.11 使用大驼峰命名拓展 - - camel_case_extensions - # 1.12 类成员,顶级定义,变量,参数,命名参数和命名构造函数 - - non_constant_identifier_names - # 1.12 常量变量,包括枚举的名称规范 - - constant_identifier_names - #倒入import library的名字规范 - - library_names - #1.13 import dart文件的规范 - - file_names - #1.21 【可选】顺序 - - directives_ordering - #2.22 【推荐】 公开接口要有注释 - - package_api_docs - #3.32【必须】组合字符串统一使用插值的形式${param},并避免在字符串插值中使用不必要的大括号 - - prefer_interpolation_to_compose_strings - #3.41 【必须】尽可能使用字面量初始化集合 - - prefer_collection_literals - #3.42【必须】使用.isEmpty/.isNotEmpty判空 - - prefer_is_empty - #3.51 【必须】不要使用lambda表达式来替代tear-off - - unnecessary_lambdas - #3.61 【必须】参数默认值用=号 - - prefer_equal_for_default_values - #3.71 【必须】不要把变量初始化为null - - avoid_init_to_null - #3.82 【必须】去掉不必要的setter和getter - - unnecessary_getters_setters - #4.35 mixin的使用规范 - - prefer_mixin - #4.54【必须】避免单独使用setter或者getters - - avoid_setters_without_getters - #4.55【必须】不给setter方法指定返回值 - - avoid_return_types_on_setters - #4.56【必须】基础数据类型的返回值不要返回null - - avoid_returning_null - #4.57 【必须】不要返回this来实现链式调用 - - avoid_returning_this - #4.61 【必须】添加必要的类型注解 TODO 讨论是否有替代方案 - # - type_annotate_public_apis - #4.66 【必须】不要使用旧的typedef语法 - - prefer_generic_function_type_aliases - #4.67 【必须】正确使用Future返回值 TODO 这个lint是说所有方法都必须需要返回类型而不是仅仅Future - # - always_declare_return_types - #4.81 【必须】必须重写 hashCode 方法 - - hash_and_equals - #4.83 【必须】可变类不要重载== - - avoid_equals_and_hash_code_on_mutable_classes - #4.84 【必须】重载==时不必判空 - - avoid_null_checks_in_equality_operators - # 声明返回类型 TODO============ 以下规则对齐codecc代码扫描 ===================================== - # - always_declare_return_types - # separate the control structure expression from its statement - - always_put_control_body_on_new_line - # 声明了@required的参数,需要有assert(param != null) - - always_require_non_null_named_parameters - # 注释重写的方法和参数 - - annotate_overrides - # 避免空的else - - avoid_empty_else - # 避免使用print方法 - # - avoid_print - # 避免以相对路径方式引入文件 - - avoid_relative_lib_imports - # 避免返回null给future - - avoid_returning_null_for_future - # 避免复写相同的参数类型 - - avoid_shadowing_type_parameters - # 避免使用同步的文件操作 - - avoid_slow_async_io - # 避免参数名和类型名相同 - - avoid_types_as_parameter_names - # 避免使用web的库在flutter终端中,这些库不支持在web以外使用 - - avoid_web_libraries_in_flutter - # dart.async.StreamSubscription的实例不用时,触发cancel方法 - - cancel_subscriptions - # dart.core.Sink的实例不用时,触发cancel方法 - - close_sinks - # 评论只引用在作用域的变量 - - comment_references - # 避免在finally语句中结束控制流 - - control_flow_in_finally - # 控制结构用大括号来区分,除非没有else语句且可放在一行的 - - curly_braces_in_flow_control_structures - # 在debug方法中引用所有的公共属性 - # - diagnostic_describe_all_properties - # 避免空的catch - - empty_catches - # 用;替代空的构造方法体 - - empty_constructor_bodies - # 避免使用空语句 - - empty_statements - # 不要出现可以在编译阶段推算出恒true或者false的条件判断 - - invariant_booleans - # 当containns方法的参数和可枚举的类型不同,不要触发可枚举类型的contains方法 - - iterable_contains_unrelated_type - # 用小写加下划线做库的命名 - - library_prefixes - # 当remove方法的参数和List的类型不同,不要触发remove方法 - - list_remove_unrelated_type - # 不要出现只有一种情况的boolean表达式 - - literal_only_boolean_expressions - # 不要在list内用临近字符串方式 - - no_adjacent_strings_in_list - # 不要用重复值的case语句 - - no_duplicate_case_values - # 不要传null值给闭包语句 - - null_closures - # 省略对本地参数的类型声明 - - omit_local_variable_types - # 用相邻的string去减少string操作符 - - prefer_adjacent_string_concatenation - # 尽量用??=来判定null - - prefer_conditional_assignment - # 不要用indexOf来判定一个集合是否包含某元素 - - prefer_contains - # 用final来声明在类中不会被改变的私有参数 - - prefer_final_fields - # 当由iterables构建map时,用for语句 - - prefer_for_elements_to_map_fromIterable - # 用?? 替代null的检查 - - prefer_if_null_operators - # 用isNotEmpty替代!isEmpty - - prefer_is_not_empty - # 用iterable.whereType() 替代 iterable.where((e) => e is T). - - prefer_iterable_whereType - # 在lib路径下,建议使用相对路径引入 - - prefer_relative_imports - # 用单引号替代双引号 - - prefer_single_quotes - # 尽量用扩展符 - - prefer_spread_collections - # 在void可以的时候不要用null - - prefer_void_to_null - # 多余的getter - - recursive_getters - # 用/// 作为注释 - - slash_for_doc_comments - # 在重载==运算符时,判定入参的类型 - - test_types_in_equals - # 避免在finally语句中抛出错误 - - throw_in_finally - # 不要对已申明类型的变量在初始化时再声明 - - type_init_formals - # 在async的方法中await返回为future的方法 - - unawaited_futures - # 避免重复的const语句 - - unnecessary_const - # 避免在创建对象时用new - - unnecessary_new - # 避免在if null语句中用null - - unnecessary_null_in_if_null_operators - # 避免不必要的语句 - - unnecessary_statements - # 当不需要避免重复时不要用this - - unnecessary_this - # 避免无关类型的相等检查 - - unrelated_type_equality_checks - # 避免直接插入html - - unsafe_html - # 用类函数语法作为参数 - - use_function_type_syntax_for_parameters - # 用rethrow去重新抛出错误 - - use_rethrow_when_possible - # 用正确的正则去创建正则 - - valid_regexps diff --git a/assets/tdesign/td_icons.ttf b/assets/tdesign/td_icons.ttf deleted file mode 100644 index 3ad6e027e..000000000 Binary files a/assets/tdesign/td_icons.ttf and /dev/null differ diff --git a/demo_tool/README.md b/demo_tool/README.md deleted file mode 100644 index 3899f04c2..000000000 --- a/demo_tool/README.md +++ /dev/null @@ -1,61 +0,0 @@ -# demo_tool -# 基于smart_cli实现的组件库生成工具 - -## 组件注释规范 -### 注:生成工具有待完善,目前先按工具代码规范编写代码,如有不满足的场景,再修改工具 -编写规范需注意: -- 构造方法为类名下的第一行代码,且不能有注释。成员字段需写在构造方法后面 -- 成员变量的注释需要用///,不能用// -- 构造方法不能标注@override - -#### 组件widget注释示例: -``` -/// 组件简介(必须) -``` -#### 组件属性注释示例: -``` -/// 属性简介(必须) -``` -#### 组件demo注释示例: -``` -/// demo名称(可以为空,为空的时候默认显示组件名称) -/// demo示例介绍(可以为空) -``` - -## 组件库工具使用方法 -### 初始化工具调用命令 -``` -dart tools/smart_cli/bin/main.dart generate - --file 相对ui_component目录的组件文件路径 - --folder 相对ui_component目录的组件文件夹路径 - --name 组件名,多个组件名之间用英文,分割 - --folder-name [可选]生成的组件示例文件夹名称,默认生成的文件夹名称是第一个name参数的下划线表示 - --[no-]only-api 是否只更新api文件 - --[no-]use-grammar 是否采用语法分析器,默认采用词法分析 -``` ---- -### 一、 初始化命令 - -【前置】:在demo_tool/version中填入对应dart_sdk的版本号 - -初始化命令有以下 3 种使用方式: - -1、初始化一个组件文件中的一个组件示例,没有--folder-name的时候,默认文件夹名称是第一个name的下划线表示,示例: - -``` -./demo_tool/bin/demo_tool generate --file lib/src/components/tags/td_tag.dart --name TDTag --folder-name tag --only-api -``` - -2、 把一个文件中的多个组件合并生成一份示例数据(api说明生成在一个文件中),没有--folder-name的时候,默认文件夹名称是第一个name的下划线表示 -``` -./demo_tool/bin/demo_tool generate --file lib/checkbox/custom_check_box.dart --name SquareCheckbox,TECheckBox --folder-name checkbox2 -``` -3、 把一个文件夹中的多个组件合并生成一份示例数据(api说明生成在一个文件中),没有--folder-name的时候,默认文件夹名称是第一个name的下划线表示 -``` -./demo_tool/bin/demo_tool generate --folder lib/setting --name SettingItemWidget,SettingTowRowCellWidget,SettingLeftTextCellWidget,SettingCheckBoxCellWidget,SettingTowTextCellWidget,SettingTowLineTextCellWidget,SettingGroupWidget,SettingGroupTextWidget --folder-name setting -``` - -如果想只更新API文档,那么在上述初始化的命令之后增加参数 `--only-api` 即可 - -默认采用词法分析,如果想采用语法分析的方式生成代码,那么在上述初始化的命令之后增加参数 `--use-grammar` 即可 -` \ No newline at end of file diff --git a/demo_tool/all_build.sh b/demo_tool/all_build.sh deleted file mode 100644 index 922eca073..000000000 --- a/demo_tool/all_build.sh +++ /dev/null @@ -1,35 +0,0 @@ -./bin/demo_tool generate --file ../lib/src/components/avatar/td_avatar.dart --name TDAvatar --folder-name avatar --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/badge/td_badge.dart --name TDBadge --folder-name badge --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/bottom_nav_bar/td_bottom_nav_bar.dart --name TDBottomNavBar --folder-name bottom_nav_bar --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/button/td_button.dart --name TDButton --folder-name button --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/checkbox/td_check_box.dart --name TDCheckbox --folder-name checkbox --output ../example/assets/api/ --only-api -# Dialog 需要多个文件生成一个api -#./bin/demo_tool generate --file ../lib/src/components/check_box/td_check_box.dart --name TDCheckbox --folder-name check_box --output ../example/assets/api/ --only-api - -./bin/demo_tool generate --file ../lib/src/components/divider/td_divider.dart --name TDDivider --folder-name divider --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/empty/td_empty.dart --name TDEmpty --folder-name empty --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/image/td_image.dart --name TDImage --folder-name image --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/input/td_input.dart --name TDInput --folder-name input --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/loading/td_loading.dart --name TDLoading --folder-name loading --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/navbar/td_nav_bar.dart --name TDNavBar --folder-name navbar --output ../example/assets/api/ --only-api -# picker需多个文件生成一个api -#./bin/demo_tool generate --file ../lib/src/components/picker/td_nav_bar.dart --name TDNavBar --folder-name nav_bar --output ../example/assets/api/ --only-api - -./bin/demo_tool generate --file ../lib/src/components/popup/td_popup_route.dart --name TDSlidePopupRoute --folder-name popup --output ../example/assets/api/ --only-api -# 需多个文件生成一个api,透彻父类参数需处理 -./bin/demo_tool generate --file ../lib/src/components/radio/td_radio.dart --name TDRadio --folder-name radio --output ../example/assets/api/ --only-api -# 有三方组件 -#./bin/demo_tool generate --file ../lib/src/components/refresh/td_refresh_header.dart --name TDRefreshHeader --folder-name refresh --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/search/td_search_bar.dart --name TDSearchBar --folder-name search --output ../example/assets/api/ --only-api -# 有三方组件 -#./bin/demo_tool generate --file ../lib/src/components/swiper/td_swiper.dart --name TDSwiperPagination --folder-name swiper --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/switch/td_switch.dart --name TDSwitch --folder-name switch --output ../example/assets/api/ --only-api -# 多个类生成一个api, 注释未生效 -./bin/demo_tool generate --file ../lib/src/components/tabbar/td_tab_bar.dart --name TDTabBar --folder-name tabbar --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/tag/td_tag.dart --name TDTag --folder-name tag --output ../example/assets/api/ --only-api -./bin/demo_tool generate --file ../lib/src/components/text/td_text.dart --name TDText --folder-name text --output ../example/assets/api/ --only-api -# 只有方法,没有变量 -./bin/demo_tool generate --file ../lib/src/components/toast/td_toast.dart --name TDToast --folder-name toast --output ../example/assets/api/ --only-api - - -./bin/demo_tool generate --file ../lib/src/theme/td_theme.dart --name TDTheme --folder-name theme --output ../example/assets/api/ --only-api \ No newline at end of file diff --git a/demo_tool/bin/demo_tool b/demo_tool/bin/demo_tool deleted file mode 100755 index 6f8ace585..000000000 Binary files a/demo_tool/bin/demo_tool and /dev/null differ diff --git a/demo_tool/version b/demo_tool/version deleted file mode 100644 index fb2c0766b..000000000 --- a/demo_tool/version +++ /dev/null @@ -1 +0,0 @@ -2.13.0 diff --git a/example/.gitignore b/example/.gitignore deleted file mode 100644 index a1345d017..000000000 --- a/example/.gitignore +++ /dev/null @@ -1,45 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# The .vscode folder contains launch configuration and tasks you configure in -# VS Code which you may wish to be included in version control, so this line -# is commented out by default. -#.vscode/ - -# Flutter/Dart/Pub related -**/doc/api/ -**/ios/Flutter/.last_build_id -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -.packages -.pub-cache/ -.pub/ -/build/ - -# Web related - -# Symbolication related -app.*.symbols - -# Obfuscation related -app.*.map.json - -# Android Studio will place build artifacts here -/android/app/debug -/android/app/profile -/android/app/release diff --git a/example/analysis_options.yaml b/example/analysis_options.yaml deleted file mode 100644 index 660a4e245..000000000 --- a/example/analysis_options.yaml +++ /dev/null @@ -1,208 +0,0 @@ -analyzer: - enable-experiment: - - extension-methods - errors: - unused_import: ignore - camel_case_types: warning - missing_required_param: error - missing_return: warning - todo: ignore - non_constant_identifier_names: warning - constant_identifier_names: warning - library_names: warning - file_names: warning - avoid_setters_without_getters: warning - avoid_return_types_on_setters: warning - avoid_returning_null: warning - avoid_returning_this: warning - prefer_generic_function_type_aliases: warning - always_declare_return_types: warning - hash_and_equals: warning - avoid_equals_and_hash_code_on_mutable_classes: warning - avoid_null_checks_in_equality_operators: warning - prefer_interpolation_to_compose_strings: warning - prefer_collection_literals: warning - prefer_is_empty: warning - unnecessary_lambdas: warning - prefer_equal_for_default_values: warning - avoid_init_to_null: warning - -linter: - rules: - - avoid_unnecessary_containers - - no_logic_in_create_state - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_const_literals_to_create_immutables - - sized_box_for_whitespace - - use_full_hex_values_for_flutter_colors - - use_key_in_widget_constructors - # 1.11 使用大驼峰命名类型 - - camel_case_types - # 1.11 使用大驼峰命名拓展 - - camel_case_extensions - # 1.12 类成员,顶级定义,变量,参数,命名参数和命名构造函数 - - non_constant_identifier_names - # 1.12 常量变量,包括枚举的名称规范 - - constant_identifier_names - #倒入import library的名字规范 - - library_names - #1.13 import dart文件的规范 - - file_names - #1.21 【可选】顺序 - - directives_ordering - #2.22 【推荐】 公开接口要有注释 - - package_api_docs - #3.32【必须】组合字符串统一使用插值的形式${param},并避免在字符串插值中使用不必要的大括号 - - prefer_interpolation_to_compose_strings - #3.41 【必须】尽可能使用字面量初始化集合 - - prefer_collection_literals - #3.42【必须】使用.isEmpty/.isNotEmpty判空 - - prefer_is_empty - #3.51 【必须】不要使用lambda表达式来替代tear-off - - unnecessary_lambdas - #3.61 【必须】参数默认值用=号 - - prefer_equal_for_default_values - #3.71 【必须】不要把变量初始化为null - - avoid_init_to_null - #3.82 【必须】去掉不必要的setter和getter - - unnecessary_getters_setters - #4.35 mixin的使用规范 - - prefer_mixin - #4.54【必须】避免单独使用setter或者getters - - avoid_setters_without_getters - #4.55【必须】不给setter方法指定返回值 - - avoid_return_types_on_setters - #4.56【必须】基础数据类型的返回值不要返回null - - avoid_returning_null - #4.57 【必须】不要返回this来实现链式调用 - - avoid_returning_this - #4.61 【必须】添加必要的类型注解 TODO 讨论是否有替代方案 - # - type_annotate_public_apis - #4.66 【必须】不要使用旧的typedef语法 - - prefer_generic_function_type_aliases - #4.67 【必须】正确使用Future返回值 TODO 这个lint是说所有方法都必须需要返回类型而不是仅仅Future - # - always_declare_return_types - #4.81 【必须】必须重写 hashCode 方法 - - hash_and_equals - #4.83 【必须】可变类不要重载== - - avoid_equals_and_hash_code_on_mutable_classes - #4.84 【必须】重载==时不必判空 - - avoid_null_checks_in_equality_operators - # 声明返回类型 TODO============ 以下规则对齐codecc代码扫描 ===================================== - # - always_declare_return_types - # separate the control structure expression from its statement - - always_put_control_body_on_new_line - # 声明了@required的参数,需要有assert(param != null) - - always_require_non_null_named_parameters - # 注释重写的方法和参数 - - annotate_overrides - # 避免空的else - - avoid_empty_else - # 避免使用print方法 - # - avoid_print - # 避免以相对路径方式引入文件 - - avoid_relative_lib_imports - # 避免返回null给future - - avoid_returning_null_for_future - # 避免复写相同的参数类型 - - avoid_shadowing_type_parameters - # 避免使用同步的文件操作 - - avoid_slow_async_io - # 避免参数名和类型名相同 - - avoid_types_as_parameter_names - # 避免使用web的库在flutter终端中,这些库不支持在web以外使用 - - avoid_web_libraries_in_flutter - # dart.async.StreamSubscription的实例不用时,触发cancel方法 - - cancel_subscriptions - # dart.core.Sink的实例不用时,触发cancel方法 - - close_sinks - # 评论只引用在作用域的变量 - - comment_references - # 避免在finally语句中结束控制流 - - control_flow_in_finally - # 控制结构用大括号来区分,除非没有else语句且可放在一行的 - - curly_braces_in_flow_control_structures - # 在debug方法中引用所有的公共属性 - # - diagnostic_describe_all_properties - # 避免空的catch - - empty_catches - # 用;替代空的构造方法体 - - empty_constructor_bodies - # 避免使用空语句 - - empty_statements - # 不要出现可以在编译阶段推算出恒true或者false的条件判断 - - invariant_booleans - # 当containns方法的参数和可枚举的类型不同,不要触发可枚举类型的contains方法 - - iterable_contains_unrelated_type - # 用小写加下划线做库的命名 - - library_prefixes - # 当remove方法的参数和List的类型不同,不要触发remove方法 - - list_remove_unrelated_type - # 不要出现只有一种情况的boolean表达式 - - literal_only_boolean_expressions - # 不要在list内用临近字符串方式 - - no_adjacent_strings_in_list - # 不要用重复值的case语句 - - no_duplicate_case_values - # 不要传null值给闭包语句 - - null_closures - # 省略对本地参数的类型声明 - - omit_local_variable_types - # 用相邻的string去减少string操作符 - - prefer_adjacent_string_concatenation - # 尽量用??=来判定null - - prefer_conditional_assignment - # 不要用indexOf来判定一个集合是否包含某元素 - - prefer_contains - # 用final来声明在类中不会被改变的私有参数 - - prefer_final_fields - # 当由iterables构建map时,用for语句 - - prefer_for_elements_to_map_fromIterable - # 用?? 替代null的检查 - - prefer_if_null_operators - # 用isNotEmpty替代!isEmpty - - prefer_is_not_empty - # 用iterable.whereType() 替代 iterable.where((e) => e is T). - - prefer_iterable_whereType - # 在lib路径下,建议使用相对路径引入 - - prefer_relative_imports - # 用单引号替代双引号 - - prefer_single_quotes - # 尽量用扩展符 - - prefer_spread_collections - # 在void可以的时候不要用null - - prefer_void_to_null - # 多余的getter - - recursive_getters - # 用/// 作为注释 - - slash_for_doc_comments - # 在重载==运算符时,判定入参的类型 - - test_types_in_equals - # 避免在finally语句中抛出错误 - - throw_in_finally - # 不要对已申明类型的变量在初始化时再声明 - - type_init_formals - # 在async的方法中await返回为future的方法 - - unawaited_futures - # 避免重复的const语句 - - unnecessary_const - # 避免在创建对象时用new - - unnecessary_new - # 避免在if null语句中用null - - unnecessary_null_in_if_null_operators - # 避免不必要的语句 - - unnecessary_statements - # 当不需要避免重复时不要用this - - unnecessary_this - # 避免无关类型的相等检查 - - unrelated_type_equality_checks - # 避免直接插入html - - unsafe_html - # 用类函数语法作为参数 - - use_function_type_syntax_for_parameters - # 用rethrow去重新抛出错误 - - use_rethrow_when_possible - # 用正确的正则去创建正则 - - valid_regexps diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle deleted file mode 100644 index 042d840d2..000000000 --- a/example/android/app/build.gradle +++ /dev/null @@ -1,62 +0,0 @@ -apply from: "version.gradle" - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - - compileSdkVersion 31 - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.tdesign.tdesign_flutter_example" - minSdkVersion 21 - targetSdkVersion 31 - versionCode createVersionCode() - versionName createVersionName() - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/example/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 10972f45b..000000000 --- a/example/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 5ad14c9a8..000000000 --- a/example/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - diff --git a/example/android/app/src/profile/AndroidManifest.xml b/example/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 10972f45b..000000000 --- a/example/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/example/android/app/version.gradle b/example/android/app/version.gradle deleted file mode 100644 index 14e822a48..000000000 --- a/example/android/app/version.gradle +++ /dev/null @@ -1,27 +0,0 @@ -import java.text.SimpleDateFormat - -def cfg = rootProject.ext.config -ext.createVersionCode = { -> - Date now = new Date() - SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd") - String nowStr = sf.format(now) - int nowInt = Integer.valueOf(nowStr) - //20210408//在指挥官全量之前,保证指挥官灰度版本大于线上全量版本,避免指挥官灰度版本被应用市场覆盖。指挥官版本最低2021041211,线上5.7.6.81版本是2021040781 - if (cfg.isCI != null && cfg.isCI.toBoolean()) { - // 蓝盾环境 - nowInt * 100 + Integer.parseInt(cfg.BuildNo) % 100 - } else { - // 本地环境 - nowInt * 100 + 1 - } -} - -ext.createVersionName = { -> - if (cfg.isCI != null && cfg.isCI.toBoolean()) { - // 蓝盾环境 - cfg.MajorVersion + "." + cfg.MinorVersion + "." + cfg.FixVersion + "." + cfg.BuildNo - } else { - // 本地环境 - "0.0.0.1" - } -} \ No newline at end of file diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index 05e6b9778..000000000 --- a/example/android/build.gradle +++ /dev/null @@ -1,60 +0,0 @@ -buildscript { - ext.kotlin_version = '1.6.21' - - ext { - - config = [ - // 蓝盾自带变量 - isCI : System.env.isCI // 是否在RMD的持续集成构建环境中 - , VersionCode : System.env.VersionCode // VersionCode,每次发版本+2 - , devopsUUID : System.env.uuid //蓝盾构建UUID - , MajorVersion : System.env.MajorVersion // 主版本号 - , MinorVersion : System.env.MinorVersion // 特性版本号 - , FixVersion : System.env.FixVersion // 修正版本号 - , BuildNo : System.env.BuildNo // 构建号 - , isPublish : System.env.isPublish // 是否是外发版本,用来控制调试入口等 - , isBeta : System.env.isBeta // 是否是灰度版本 - , gitVersion : System.env.gitVersion // git提交hash - , keyAlias : System.env.keyAlias // 签名信息 - , storePassword: System.env.storePassword // storePassword - , abi_filters: System.env.abi_filters // abi类型配置 - ] - - def dir = System.getProperty("user.dir") - def file = new File(dir, 'local.properties') - Properties properties = new Properties() - if (file.exists()) { - properties.load(file.newDataInputStream()) - } - enableWatchman = Boolean.valueOf(properties.getProperty('enableWatchman', "")) - } - - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index 44e62bcf0..000000000 --- a/example/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/assets/api/avatar_api.md b/example/assets/api/avatar_api.md deleted file mode 100644 index 287742ecf..000000000 --- a/example/assets/api/avatar_api.md +++ /dev/null @@ -1,14 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| key | Key | - | | -| size | TDAvatarSize | TDAvatarSize.medium | 头像尺寸 | -| type | TDAvatarType | TDAvatarType.normal | 头像类型 | -| text | String? | - | 自定义文字 | -| avatarUrl | String? | - | 头像地址 | -| avatarSize | double? | - | 自定义头像大小 | -| avatarDisplayList | List? | - | 带操作\展示的头像列表 | -| displayText | String? | - | 纯展示类型末尾文字 | -| onTap | Function()? | - | 操作点击事件 | -| defaultUrl | String | '' | 默认图片 | diff --git a/example/assets/api/badge_api.md b/example/assets/api/badge_api.md deleted file mode 100644 index a01b56be4..000000000 --- a/example/assets/api/badge_api.md +++ /dev/null @@ -1,15 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| type | TDBadgeType | type | 红点样式 | -| key | Key | - | | -| count | String? | - | 红点数量 | -| border | TDBadgeBorder | TDBadgeBorder.large | 红点圆角大小 | -| color | Color? | - | 红点颜色 | -| textColor | Color? | - | 文字颜色 | -| redPointSize | double | 10 | 红点大小 | -| message | String? | - | 消息内容 | -| widthLarge | double | 32 | 角标大三角形宽 | -| widthSmall | double | 12 | 角标小三角形宽 | -| padding | EdgeInsetsGeometry? | - | 角标自定义padding | diff --git a/example/assets/api/bottom_nav_bar_api.md b/example/assets/api/bottom_nav_bar_api.md deleted file mode 100644 index 29a179a16..000000000 --- a/example/assets/api/bottom_nav_bar_api.md +++ /dev/null @@ -1,14 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| type | TDBottomNavBarType | type | | -| key | Key | - | | -| navigationTabs | List | - | tab | -| barHeight | double? | _kDefaultNavBarHeight | tab高度 | -| useVerticalDivider | bool? | - | 是否使用竖线分隔 | -| dividerHeight | double? | - | 分割线高度(可选) | -| dividerThickness | double? | - | 分割线厚度(可选) | -| dividerColor | Color? | - | 分割线颜色(可选) | -| showTopBorder | bool? | true | 是否展示bar上边线(设置为true 但是topBorder样式未设置,则使用默认值) | -| topBorder | BorderSide? | - | 上边线样式 | diff --git a/example/assets/api/button_api.md b/example/assets/api/button_api.md deleted file mode 100644 index 06f0bcf33..000000000 --- a/example/assets/api/button_api.md +++ /dev/null @@ -1,18 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| key | Key | - | | -| content | String? | - | 文本内容 | -| size | TDButtonSize | TDButtonSize.medium | 尺寸 | -| child | Widget? | - | 自控件 | -| disabled | bool | false | 禁止点击 | -| style | TDButtonStyle? | - | 样式,强按钮,弱按钮,警告按钮等 | -| textStyle | TextStyle? | - | 自定义可点击状态文本样式 | -| disableTextStyle | TextStyle? | - | 自定义不可点击状态文本样式 | -| width | double? | - | 自定义宽度 | -| height | double? | - | 自定义高度 | -| onTap | TDButtonEvent? | - | 点击事件 | -| icon | IconData? | - | 图标icon | -| onLongPress | TDButtonEvent? | - | 长按事件 | -| padding | EdgeInsetsGeometry? | - | 自定义padding | diff --git a/example/assets/api/checkbox_api.md b/example/assets/api/checkbox_api.md deleted file mode 100644 index 5b676d000..000000000 --- a/example/assets/api/checkbox_api.md +++ /dev/null @@ -1,20 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| id | String? | - | id | -| key | Key | - | | -| title | String? | - | 文本 | -| subTitle | String? | - | 辅助文字 | -| enable | bool | true | 不可用 | -| checked | bool | false | 选中状态。默认为`false` | -| titleMaxLine | int? | - | 标题的行数 | -| subTitleMaxLine | int? | 1 | 辅助文字的行数 | -| customIconBuilder | IconBuilder? | - | 自定义Checkbox显示样式 | -| customContentBuilder | ContentBuilder? | - | 完全自定义内容 | -| style | TDCheckboxStyle? | - | 复选框样式:圆形或方形 | -| spacing | double? | - | icon和文字的距离 | -| backgroundColor | Color? | - | 背景颜色 | -| size | TDCheckBoxSize | TDCheckBoxSize.small | 复选框大小 | -| contentDirection | TDContentDirection | TDContentDirection.right | 文字相对icon的方位 | -| onCheckBoxChanged | OnCheckValueChanged? | - | 切换监听 | diff --git a/example/assets/api/default_api.md b/example/assets/api/default_api.md deleted file mode 100644 index 0d957df5f..000000000 --- a/example/assets/api/default_api.md +++ /dev/null @@ -1,3 +0,0 @@ -## API - -暂无对应api diff --git a/example/assets/api/empty_api.md b/example/assets/api/empty_api.md deleted file mode 100644 index 436dda9fa..000000000 --- a/example/assets/api/empty_api.md +++ /dev/null @@ -1,10 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| type | TDEmptyType | TDEmptyType.plain | | -| image | Widget? | - | | -| emptyText | String? | - | | -| operationText | String? | - | | -| onTapEvent | TDTapEvent? | - | 点击事件 | -| key | Key | - | | diff --git a/example/assets/api/image_api.md b/example/assets/api/image_api.md deleted file mode 100644 index 1df8cb495..000000000 --- a/example/assets/api/image_api.md +++ /dev/null @@ -1,29 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| imgUrl | String | imgUrl | 图片地址 | -| key | Key | - | | -| size | TDImageSize | TDImageSize.l | 图片大小 | -| type | TDImageType | TDImageType.roundedSquare | 图片类型 | -| errorWidget | Widget? | - | 失败自定义提示 | -| loadingWidget | Widget? | - | 加载自定义提示 | -| width | double? | - | 自定义宽 | -| height | double? | - | 自定义高 | -| frameBuilder | ImageFrameBuilder? | - | 以下系统Image属性,释义请参考系统[Image]中注释 | -| loadingBuilder | ImageLoadingBuilder? | - | | -| errorBuilder | ImageErrorWidgetBuilder? | - | | -| semanticLabel | String? | - | | -| excludeFromSemantics | bool | false | | -| color | Color? | - | | -| opacity | Animation? | - | | -| colorBlendMode | BlendMode? | - | | -| alignment | AlignmentGeometry | Alignment.center | | -| repeat | ImageRepeat | ImageRepeat.noRepeat | | -| centerSlice | Rect? | - | | -| matchTextDirection | bool | false | | -| gaplessPlayback | bool | false | | -| isAntiAlias | bool | false | | -| filterQuality | FilterQuality | FilterQuality.low | | -| cacheHeight | int? | - | | -| cacheWidth | int? | - | | diff --git a/example/assets/api/input_api.md b/example/assets/api/input_api.md deleted file mode 100644 index 1decfbbd8..000000000 --- a/example/assets/api/input_api.md +++ /dev/null @@ -1,37 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| key | Key | - | | -| width | double? | - | 输入框宽度 | -| textStyle | TextStyle? | - | 文本颜色 | -| backgroundColor | Color? | - | 输入框背景色 | -| decoration | Decoration? | - | 输入框样式 | -| leftLabel | String? | - | 输入框左侧文案 | -| readOnly | bool | false | 是否只读 | -| autofocus | bool | false | 是否自动获取焦点 | -| obscureText | bool | false | 是否隐藏输入的文字,一般用在密码输入框中 | -| onEditingComplete | VoidCallback? | - | 点击键盘完成按钮时触发的回调 | -| onSubmitted | ValueChanged? | - | 点击键盘完成按钮时触发的回调, 参数值为输入的内容 | -| hintText | String? | - | 提示文案 | -| inputType | TextInputType? | - | 键盘类型,数字、字母 | -| onChanged | ValueChanged? | - | 输入文本变化时回调 | -| inputFormatters | List? | - | 显示输入内容,如限制长度(LengthLimitingTextInputFormatter(6)) | -| inputDecoration | InputDecoration? | - | 自定义输入框样式,默认圆角 | -| maxLines | int | 1 | 最大输入行数 | -| focusNode | FocusNode? | - | 获取或者取消焦点使用 | -| controller | TextEditingController? | - | controller 用户获取或者赋值输入内容 | -| cursorColor | Color? | - | 游标颜色 | -| rightBtn | Widget? | - | 右侧按钮 | -| hintTextStyle | TextStyle? | - | 提示文本颜色,默认为文本颜色 | -| onTapBtn | GestureTapCallback? | - | 右侧按钮点击 | -| labelWidget | Widget? | - | leftLabel右侧组件,支持自定义 | -| textInputBackgroundColor | Color? | - | 文本框背景色 | -| contentPadding | EdgeInsetsGeometry? | - | textInput内边距 | -| type | TDInputType | TDInputType.normal | 输入框类型 | -| size | TDInputSize | TDInputSize.small | 输入框规格 | -| inputWidth | double? | 81 | 输入框宽度 | -| maxNum | int? | 500 | 最大字数限制 | -| errorText | String? | '' | 错误提示信息 | -| textAlign | TextAlign? | - | 文字对齐方向 | -| rightWidget | Widget? | - | 右侧自定义组件 特殊类型时生效 | diff --git a/example/assets/api/loading_api.md b/example/assets/api/loading_api.md deleted file mode 100644 index 65c5375e7..000000000 --- a/example/assets/api/loading_api.md +++ /dev/null @@ -1,12 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| key | Key | - | | -| size | TDLoadingSize | - | | -| icon | TDLoadingIcon? | - | | -| iconColor | Color? | - | | -| axis | Axis | Axis.vertical | | -| text | String? | - | | -| customIcon | Widget? | - | | -| textColor | Color | Colors.black | | diff --git a/example/assets/api/navbar_api.md b/example/assets/api/navbar_api.md deleted file mode 100644 index aec8521c6..000000000 --- a/example/assets/api/navbar_api.md +++ /dev/null @@ -1,24 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| key | Key | - | | -| leftBarItems | List? | - | | -| rightBarItems | List? | - | | -| titleWidget | Widget? | - | | -| title | String? | - | | -| titleColor | Color? | - | | -| titleFont | Font? | - | | -| titleFontFamily | FontFamily? | - | | -| titleFontWeight | FontWeight? | FontWeight.w500 | | -| centerTitle | bool | true | | -| opacity | double | 1.0 | | -| backgroundColor | Color? | - | | -| titleMargin | double | 16 | 中间文案左右两边间距 | -| padding | EdgeInsetsGeometry? | - | | -| height | double | 44 | | -| screenAdaptation | bool | true | 是否进行屏幕适配,默认true | -| useDefaultBack | bool | true | 是否使用默认的返回 | -| onBack | VoidCallback? | - | 返回事件 | -| useBorderStyle | bool | false | 是否使用边框模式 | -| border | TDNavBarItemBorder? | - | 边框 | diff --git a/example/assets/api/popup_api.md b/example/assets/api/popup_api.md deleted file mode 100644 index ec3de0555..000000000 --- a/example/assets/api/popup_api.md +++ /dev/null @@ -1,10 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| builder | WidgetBuilder | - | | -| barrierLabel | String? | - | | -| modalBarrierColor | Color? | - | | -| isDismissible | bool | true | | -| transitionAnimationController | AnimationController? | - | | -| slideTransitionFrom | SlideTransitionFrom | SlideTransitionFrom.bottom | | diff --git a/example/assets/api/radio_api.md b/example/assets/api/radio_api.md deleted file mode 100644 index 4885d98a3..000000000 --- a/example/assets/api/radio_api.md +++ /dev/null @@ -1,18 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| id | | - | | -| key | Key | - | | -| title | | - | | -| subTitle | | - | | -| enable | | true | | -| subTitleMaxLine | | 1 | | -| titleMaxLine | | 1 | | -| checkedColor | | - | | -| customContentBuilder | | - | | -| spacing | | - | | -| size | | TDCheckBoxSize.small | | -| radioStyle | | TDRadioStyle.circle | | -| contentDirection | | TDContentDirection.right | | -| customIconBuilder | | - | | diff --git a/example/assets/api/refresh_api.md b/example/assets/api/refresh_api.md deleted file mode 100644 index 0df556242..000000000 --- a/example/assets/api/refresh_api.md +++ /dev/null @@ -1,3 +0,0 @@ -## web示例暂不可操作,请在移动端体验 - - diff --git a/example/assets/api/search_api.md b/example/assets/api/search_api.md deleted file mode 100644 index 452bdcd28..000000000 --- a/example/assets/api/search_api.md +++ /dev/null @@ -1,11 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| key | Key | - | | -| placeHolder | String? | - | | -| onTextChanged | TDSearchBarEvent? | - | | -| onSubmitted | TDSearchBarEvent? | - | | -| onEditComplete | TDSearchBarCallBack? | - | | -| autoFocus | bool | false | | -| backgroundColor | Color? | Colors.white | | diff --git a/example/assets/api/switch_api.md b/example/assets/api/switch_api.md deleted file mode 100644 index 12b34867d..000000000 --- a/example/assets/api/switch_api.md +++ /dev/null @@ -1,10 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| key | Key | - | | -| enable | bool | true | 是否可点击 | -| isOn | bool | false | 是否打开 | -| onColor | Color? | - | 开启颜色 | -| offColor | Color? | - | 关闭颜色 | -| onChanged | ValueChanged? | - | 改变事件 | diff --git a/example/assets/api/tabbar_api.md b/example/assets/api/tabbar_api.md deleted file mode 100644 index 5942d6f74..000000000 --- a/example/assets/api/tabbar_api.md +++ /dev/null @@ -1,26 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| key | Key | - | | -| tabs | List | - | tab数组 | -| controller | TabController? | - | tab控制器 | -| decoration | Decoration? | - | tabBar修饰 | -| backgroundColor | Color? | - | tabBar背景色 | -| indicatorColor | Color? | - | tabBar下标颜色 | -| indicatorWidth | double? | - | tabBar下标宽度 | -| indicatorHeight | double? | - | tabBar下标高度 | -| labelColor | Color? | - | tabBar 已选标签颜色 | -| unselectedLabelColor | Color? | - | tabBar未选标签颜色 | -| isScrollable | bool | false | 是否滚动 | -| unselectedLabelStyle | TextStyle? | - | unselectedLabel字体 | -| labelStyle | TextStyle? | - | 已选label字体 | -| width | double? | - | tabBar宽度 | -| height | double? | - | tabBar高度 | -| indicatorPadding | EdgeInsets? | - | 引导padding | -| labelPadding | EdgeInsetsGeometry? | - | tab间距 | -| indicator | Decoration? | - | 自定义引导控件 | -| physics | ScrollPhysics? | - | 自定义滑动 | -| onTap | Function(int)? | - | 点击事件 | -| isVertical | bool | false | 是否是竖向 | -| showIndicator | bool | false | 是否展示引导控件 | diff --git a/example/assets/api/tag_api.md b/example/assets/api/tag_api.md deleted file mode 100644 index 52dafc464..000000000 --- a/example/assets/api/tag_api.md +++ /dev/null @@ -1,17 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| text | String | text | 标签内容 | -| textColor | Color? | - | 文字颜色, 优先级高于style的textColor | -| backgroundColor | Color? | - | 背景颜色, 优先级高于style的backgroundColor | -| font | Font? | - | 字体尺寸, 优先级高于style的font | -| fontWeight | FontWeight? | - | 字体粗细, 优先级高于style的fontWeight | -| style | TDTagStyle? | - | 标签样式 | -| size | TDTagSize | TDTagSize.middle | 标签大小 | -| padding | EdgeInsets? | - | 自定义模式下的间距 | -| forceVerticalCenter | bool | true | 是否强制中文文字居中 | -| needCloseIcon | bool | false | 关闭图标 | -| onCloseTap | GestureTapCallback? | - | 关闭图标点击事件 | -| overflow | TextOverflow? | - | 文字溢出处理 | -| key | Key | - | | diff --git a/example/assets/api/text_api.md b/example/assets/api/text_api.md deleted file mode 100644 index f2e06eff2..000000000 --- a/example/assets/api/text_api.md +++ /dev/null @@ -1,27 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| data | null | data | 以下系统text属性,释义请参考系统[Text]中注释 | -| font | Font? | - | 字体尺寸,包含大小size和行高height | -| fontWeight | FontWeight? | FontWeight.w400 | 字体粗细 | -| fontFamily | FontFamily? | - | 字体ttf | -| textColor | Color | Colors.black | 文本颜色 | -| backgroundColor | Color? | - | 背景颜色 | -| isTextThrough | bool? | false | 是否是横线穿过样式(删除线) | -| lineThroughColor | Color? | Colors.white | 删除线颜色,对应TestStyle的decorationColor | -| package | String? | - | 字体包名 | -| style | TextStyle? | - | 自定义的TextStyle,其中指定的属性,将覆盖扩展的外层属性 | -| strutStyle | StrutStyle? | - | | -| textAlign | TextAlign? | - | | -| textDirection | TextDirection? | - | | -| locale | Locale? | - | | -| softWrap | bool? | - | | -| overflow | TextOverflow? | - | | -| textScaleFactor | double? | - | | -| maxLines | int? | - | | -| semanticsLabel | String? | - | | -| textWidthBasis | TextWidthBasis? | - | | -| textHeightBehavior | ui.TextHeightBehavior? | - | | -| forceVerticalCenter | bool | false | | -| key | Key | - | | diff --git a/example/assets/api/theme_api.md b/example/assets/api/theme_api.md deleted file mode 100644 index 3ebd06886..000000000 --- a/example/assets/api/theme_api.md +++ /dev/null @@ -1,8 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | -| data | TDThemeData | - | | -| child | Widget | - | | -| systemData | ThemeData? | - | Flutter系统主题数据 | -| key | Key | - | | diff --git a/example/assets/api/toast_api.md b/example/assets/api/toast_api.md deleted file mode 100644 index 7a330802e..000000000 --- a/example/assets/api/toast_api.md +++ /dev/null @@ -1,4 +0,0 @@ -## API - -| 参数 | 类型 | 默认值 | 说明 | -| --- | --- | --- | --- | diff --git a/example/assets/version b/example/assets/version deleted file mode 100644 index b87eee815..000000000 --- a/example/assets/version +++ /dev/null @@ -1 +0,0 @@ -0.0.0.1 \ No newline at end of file diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig deleted file mode 100644 index ec97fc6f3..000000000 --- a/example/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig deleted file mode 100644 index c4855bfe2..000000000 --- a/example/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock deleted file mode 100644 index 309cd00c4..000000000 --- a/example/ios/Podfile.lock +++ /dev/null @@ -1,16 +0,0 @@ -PODS: - - Flutter (1.0.0) - -DEPENDENCIES: - - Flutter (from `Flutter`) - -EXTERNAL SOURCES: - Flutter: - :path: Flutter - -SPEC CHECKSUMS: - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - -PODFILE CHECKSUM: 8e679eca47255a8ca8067c4c67aab20e64cb974d - -COCOAPODS: 1.10.1 diff --git a/example/lib/main.dart b/example/lib/main.dart deleted file mode 100644 index a706297a0..000000000 --- a/example/lib/main.dart +++ /dev/null @@ -1,297 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:tdesign_flutter/src/util/platform_util.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import 'tdesign/example_base.dart'; -import 'tdesign/example_route.dart'; -import 'tdesign/page/td_avatar_page.dart'; -import 'tdesign/page/td_badge_page.dart'; -import 'tdesign/page/td_bottom_nav_bar_page.dart'; -import 'tdesign/page/td_button_page.dart'; -import 'tdesign/page/td_checkbox_page.dart'; -import 'tdesign/page/td_date_picker_page.dart'; -import 'tdesign/page/td_dialog_page.dart'; -import 'tdesign/page/td_divider_page.dart'; -import 'tdesign/page/td_empty_page.dart'; -import 'tdesign/page/td_icon_page.dart'; -import 'tdesign/page/td_image_page.dart'; -import 'tdesign/page/td_input_page.dart'; -import 'tdesign/page/td_loading_page.dart'; -import 'tdesign/page/td_navbar_page.dart'; -import 'tdesign/page/td_picker_page.dart'; -import 'tdesign/page/td_popup_page.dart'; -import 'tdesign/page/td_radio_page.dart'; -import 'tdesign/page/td_refresh_page.dart'; -import 'tdesign/page/td_search_bar_page.dart'; -import 'tdesign/page/td_swiper_page.dart'; -import 'tdesign/page/td_switch_page.dart'; -import 'tdesign/page/td_tabbar_page.dart'; -import 'tdesign/page/td_tag_page.dart'; -import 'tdesign/page/td_text_page.dart'; -import 'tdesign/page/td_theme_page.dart'; -import 'tdesign/page/td_toast_page.dart'; -import 'web/web.dart' if(dart.library.io) 'web/web_replace.dart' as web; - -PageBuilder _wrapInheritedTheme(WidgetBuilder builder){ - return (context, model){ - return ExamplePageInheritedTheme(model: model, child: builder(context)); - }; -} - -/// 新增的示例页面,在此增加模型即可,会自动注册增加按钮。示例页面编写参考TDTextPage() -List examplePageList = [ - ExamplePageModel( - text: '文本控件--基础', - path: 'TDTextPage', - apiPath: 'text', - pageBuilder: _wrapInheritedTheme((context) => const TDTextPage())), - ExamplePageModel( - text: '图标--基础', - path: 'TDIconPage', - codePath: 'icon', - pageBuilder: _wrapInheritedTheme((context) => const TDIconPage())), - ExamplePageModel( - text: '主题--基础', - path: 'TDThemePage', - apiPath: 'theme', - pageBuilder: _wrapInheritedTheme((context) => const TDThemePage())), - ExamplePageModel( - text: '按钮 Button', - path: 'TDButtonPage', - apiPath: 'button', - pageBuilder: _wrapInheritedTheme((context) => const TDButtonPage())), - ExamplePageModel( - text: '分割线 Divider', - path: 'TDDividerPage', - apiPath: 'divider', - pageBuilder: _wrapInheritedTheme((context) => const TDDividerPage())), - ExamplePageModel( - text: '头像 Avatar', - path: 'TDAvatarPage', - apiPath: 'avatar', - pageBuilder: _wrapInheritedTheme((context) => const TDAvatarPage())), - ExamplePageModel( - text: '徽标 Badge', - path: 'TDBadgePage', - apiPath: 'badge', - pageBuilder: _wrapInheritedTheme((context) => const TDBadgePage())), - ExamplePageModel( - text: '空状态 Empty', - path: 'TDEmptyPage', - apiPath: 'empty', - pageBuilder: _wrapInheritedTheme((context) => const TDEmptyPage())), - ExamplePageModel( - text: '图片 Image', - path: 'TDImagePage', - apiPath: 'image', - pageBuilder: _wrapInheritedTheme((context) => const TDImagePage())), - ExamplePageModel( - text: '轮播图 Swiper', - path: 'TDSwiperPage', - apiPath: 'swiper', - pageBuilder: _wrapInheritedTheme((context) => const TDSwiperPage())), - ExamplePageModel( - text: '标签 Tag', - path: 'TDTagPage', - apiPath: 'tag', - pageBuilder: _wrapInheritedTheme((context) => const TDTagPage())), - ExamplePageModel( - text: '多选框 Checkbox', - path: 'TDCheckboxPage', - apiPath: 'checkbox', - pageBuilder: _wrapInheritedTheme((context) => const TDCheckboxPage())), - ExamplePageModel( - text: '时间选择器 DatePicker', - path: 'TDDatePickerPage', - codePath: 'date_picker', - pageBuilder: _wrapInheritedTheme((context) => const TDDatePickerPage())), - ExamplePageModel( - text: '输入框 Input', - path: 'TDInputViewPag', - apiPath: 'input', - pageBuilder: _wrapInheritedTheme((context) => const TDInputViewPage())), - ExamplePageModel( - text: '选择器 Picker', - path: 'TDPickerPage', - codePath: 'picker', - pageBuilder: _wrapInheritedTheme((context) => const TDPickerPage())), - ExamplePageModel( - text: '单选框 Radio', - path: 'TDRadioPage', - apiPath: 'radio', - pageBuilder: _wrapInheritedTheme((context) => const TDRadioPage())), - ExamplePageModel( - text: '搜索框 Search', - path: 'TDSearchBarPage', - apiPath: 'search', - codePath: 'search_bar', - pageBuilder: _wrapInheritedTheme((context) => const TDSearchBarPage())), - ExamplePageModel( - text: '开关 Switch', - path: 'TDSwitchPage', - apiPath: 'switch', - pageBuilder: _wrapInheritedTheme((context) => const TDSwitchPage())), - ExamplePageModel( - text: '导航栏 NavBar', - path: 'TDNavBarPage', - apiPath: 'navbar', - pageBuilder: _wrapInheritedTheme((context) => const TDNavBarPage())), - ExamplePageModel( - text: '标签栏 TabBar', - path: 'TDBottomNavBarPage', - apiPath: 'bottom_nav_bar', - pageBuilder: _wrapInheritedTheme((context) => const TDBottomNavBarPage())), - ExamplePageModel( - text: '选项卡 Tabs', - path: 'TDTabBarPage', - apiPath: 'tabbar', - pageBuilder: _wrapInheritedTheme((context) => const TDTabBarPage())), - ExamplePageModel( - text: '对话框 Dialog', - path: 'TDDialogPage', - apiPath: 'dialog', - pageBuilder: _wrapInheritedTheme((context) => const TDDialogPage())), - ExamplePageModel( - text: '加载 Loading', - path: 'TDLoadingPage', - apiPath: 'loading', - pageBuilder: _wrapInheritedTheme((context) => const TDLoadingPage())), - ExamplePageModel( - text: '弹出层 PopUp', - path: 'TDPopUpPage', - codePath: 'popup', - pageBuilder: _wrapInheritedTheme((context) => const TDPopupPage())), - ExamplePageModel( - text: '下拉刷新 PullDownRefresh', - path: 'TdPullDownRefreshPage', - apiPath: 'refresh', - pageBuilder: _wrapInheritedTheme((context) => const TdPullDownRefreshPage())), - ExamplePageModel( - text: '轻提示 Toast', - path: 'TDToastPage', - codePath: 'toast', - pageBuilder: _wrapInheritedTheme((context) => const TDToastPage())), -]; - -void main() { - runApp(const MyApp()); - - SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( - statusBarColor: Colors.transparent, - systemNavigationBarColor: Colors.transparent, - systemNavigationBarDividerColor: Colors.transparent, - )); -} - -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return - // 在此处导入默认主题 - TDTheme( - data: TDThemeData.defaultData(), - child: MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - colorScheme: ColorScheme.light( - primary: TDTheme.of(context).brandNormalColor)), - home: const MyHomePage(title: 'TDesgin Flutter 组件库'), - onGenerateRoute: TDExampleRoute.onGenerateRoute, - // TODO:所有路径指向首页,需区分 - routes: { - for(var model in examplePageList) - model.path: (context)=>model.pageBuilder.call(context, model) - }, - ), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({Key? key, required this.title}) : super(key: key); - - final String title; - - @override - _MyHomePageState createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - bool useConch = false; - - @override - void initState() { - super.initState(); - TDExampleRoute.init(); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - actions: PlatformUtil.isWeb ? null : [ - GestureDetector( - child: Container( - alignment: Alignment.centerRight, - padding: const EdgeInsets.only( - right: 16, - ), - child: TDText( - '关于', - textColor: TDTheme.of(context).whiteColor1, - ), - ), - onTap: () { - Navigator.pushNamed(context, TDExampleRoute.aboutPath); - }, - ) - ], - ), - body: _buildBody(context)); - } - - Widget _buildBody(BuildContext context) { - if (PlatformUtil.isWeb) { - return const web.WebMainBody(); - } - return Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: _buildChildren(context), - ), - ), - ); - } - - List _buildChildren(BuildContext context) { - return [ - for (var model in examplePageList) - Padding( - padding: const EdgeInsets.all(8.0), - child: TDButton( - style: TDButtonStyle.weakly(), - size: TDButtonSize.small, - onTap: () { - Navigator.pushNamed(context, model.path); - }, - content: model.text), - ) - ]; - } -} diff --git a/example/lib/tdesign/api_widget.dart b/example/lib/tdesign/api_widget.dart deleted file mode 100644 index 165b6eb76..000000000 --- a/example/lib/tdesign/api_widget.dart +++ /dev/null @@ -1,67 +0,0 @@ - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:markdown/markdown.dart' as md; -import 'package:tdesign_flutter/td_export.dart'; - -class ApiWidget extends StatefulWidget { - const ApiWidget({Key? key, required this.apiName, this.visible = false}) : super(key: key); - - final bool visible; - - final String? apiName; - - @override - State createState() => _ApiWidgetState(); -} - -class _ApiWidgetState extends State { - - String? result; - String? lastApiName; - - @override - Widget build(BuildContext context) { - return Visibility( - visible: widget.visible, - child: FutureBuilder( - future: getApiData(), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - return Markdown( - padding: EdgeInsets.zero, - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - selectable: true, - data: snapshot.data ?? '', - extensionSet: md.ExtensionSet( - md.ExtensionSet.gitHubWeb.blockSyntaxes, - [md.EmojiSyntax(), ...md.ExtensionSet.gitHubWeb.inlineSyntaxes], - ), - ); - } else { - return Container( - alignment: Alignment.topLeft, - child: const TDText('加载中…'), - ); - } - }, - )); - } - - Future getApiData() async { - const defaultResult = '加载失败'; - if(widget.apiName == lastApiName && result != null && result != defaultResult){ - return result!; - } - try { - var apiName = widget.apiName ?? 'default'; - result = await rootBundle.loadString('assets/api/${apiName}_api.md'); - lastApiName = widget.apiName; - } catch (e) { - print('getApiData error: $e'); - } - return result ?? defaultResult; - } -} \ No newline at end of file diff --git a/example/lib/tdesign/example_base.dart b/example/lib/tdesign/example_base.dart deleted file mode 100644 index 2b580e2f0..000000000 --- a/example/lib/tdesign/example_base.dart +++ /dev/null @@ -1,132 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/src/util/platform_util.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import 'api_widget.dart'; - - -typedef PageBuilder = Widget Function(BuildContext context, ExamplePageModel model); - -/// 示例页面数据 -class ExamplePageModel{ - - ExamplePageModel({required this.text,required this.path, this.apiPath, this.codePath,required this.pageBuilder,}); - - final String text; - final String path; - final String? apiPath; - final String? codePath; - final PageBuilder pageBuilder; -} - -/// 示例页面控件,建议每个页面返回一个ExampleWidget即可,不用独自封装 -class ExampleWidget extends StatefulWidget { - const ExampleWidget({Key? key, required this.title, required this.children, this.padding, this.backgroundColor,}) : super(key: key); - - final String title; - final List children; - final EdgeInsetsGeometry? padding; - final Color? backgroundColor; - - @override - State createState() => _ExampleWidgetState(); -} - -class _ExampleWidgetState extends State { - - late var list; - bool apiVisible = false; - ExamplePageModel? model; - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - - var modelTheme = context.dependOnInheritedWidgetOfExactType(); - model = modelTheme?.model; - - }); - list = [ - for(var item in widget.children) - Container( - padding: widget.padding ?? const EdgeInsets.all(16), - child: item, - ) - ]; - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: widget.backgroundColor, - appBar: AppBar(title: Text('${widget.title}示例页'), - actions: PlatformUtil.isWeb ? null : [ - GestureDetector( - child: Container( - alignment: Alignment.centerRight, - padding: const EdgeInsets.only(right: 32), - child: TDText('Api >>',textColor: TDTheme.of(context).whiteColor1,), - ), - onTap: (){ - setState(() { - apiVisible = !apiVisible; - }); - }, - ) - ], - ), - body: Align( - alignment: Alignment.topCenter, - child: ScrollbarTheme( - data: ScrollbarThemeData(trackVisibility: MaterialStateProperty.all(true)), - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - ApiWidget(apiName: model?.apiPath,visible: apiVisible,), ...list, - ], - ),), - ), - ), - ); - } -} - - -/// 示例样例,建议尽量使用该控件,写清晰说明内容 -class ExampleItem extends StatelessWidget{ - - const ExampleItem({Key? key, required this.desc, required this.builder}) : super(key: key); - - final String desc; - - final WidgetBuilder builder; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('示例——$desc', style: const TextStyle(color: Colors.black45),), - builder(context), - ], - ); - } -} - -/// 存储主题数据的内部控件 -class ExamplePageInheritedTheme extends InheritedWidget { - final ExamplePageModel model; - - const ExamplePageInheritedTheme( - {required this.model, Key? key, required Widget child}) - : super(key: key, child: child); - - @override - bool updateShouldNotify(covariant ExamplePageInheritedTheme oldWidget) { - return model != oldWidget.model; - } -} - diff --git a/example/lib/tdesign/example_route.dart b/example/lib/tdesign/example_route.dart deleted file mode 100644 index 6351135fc..000000000 --- a/example/lib/tdesign/example_route.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter_example/about.dart'; -import '../main.dart'; -import 'example_base.dart'; - - -class TDExampleRoute { - static final Map pageModelList = {}; - static const String aboutPath = 'about'; - - static void init(){ - for(var model in examplePageList){ - pageModelList[model.path] = model; - } - // 添加关于页路由 - pageModelList[aboutPath] = ExamplePageModel( - text: '关于', - path: 'AboutPage', - pageBuilder: (context,model)=> const AboutPage()); - } - - static Route? onGenerateRoute(RouteSettings settings) { - final name = settings.name ?? 'unknown'; - var model = pageModelList[name]; - if (model != null) { - final Route route = MaterialPageRoute( - settings: settings, - builder: (context) => model.pageBuilder(context, model)); - return route; - } else { - return MaterialPageRoute( - settings: settings, - builder: (context) => const Center( - child: Text('error'), - )); - } - } -} diff --git a/example/lib/tdesign/page/td_avatar_page.dart b/example/lib/tdesign/page/td_avatar_page.dart deleted file mode 100644 index 39f0d9f25..000000000 --- a/example/lib/tdesign/page/td_avatar_page.dart +++ /dev/null @@ -1,196 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - - -class TDAvatarPage extends StatefulWidget { - const TDAvatarPage({Key? key}) : super(key: key); - - @override - State createState() => _TDAvatarPageState(); -} - -class _TDAvatarPageState extends State { - @override - Widget build(BuildContext context) { - var imgUrl = 'https://photo.16pic.com/00/53/26/16pic_5326745_b.jpg'; - return ExampleWidget( - backgroundColor: TDTheme.of(context).whiteColor1, - title: '头像 Avatar', - children: [ - ExampleItem( - desc: '类型--默认', - builder: (_) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDAvatar( - size: TDAvatarSize.large, - type: TDAvatarType.normal, - avatarUrl: imgUrl), - TDAvatar( - size: TDAvatarSize.medium, - type: TDAvatarType.normal, - avatarUrl: imgUrl), - TDAvatar( - size: TDAvatarSize.small, - type: TDAvatarType.normal, - avatarUrl: imgUrl), - ], - ); - }), - ExampleItem( - desc: '类型-圆形', - builder: (_) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDAvatar( - size: TDAvatarSize.large, - type: TDAvatarType.circle, - avatarUrl: imgUrl), - TDAvatar( - size: TDAvatarSize.medium, - type: TDAvatarType.circle, - avatarUrl: imgUrl), - TDAvatar( - size: TDAvatarSize.small, - type: TDAvatarType.circle, - avatarUrl: imgUrl), - ], - ); - }), - ExampleItem( - desc: '类型-方形', - builder: (_) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDAvatar( - size: TDAvatarSize.large, - type: TDAvatarType.square, - avatarUrl: imgUrl), - TDAvatar( - size: TDAvatarSize.medium, - type: TDAvatarType.square, - avatarUrl: imgUrl), - TDAvatar( - size: TDAvatarSize.small, - type: TDAvatarType.square, - avatarUrl: imgUrl), - ], - ); - }), - ExampleItem( - desc: '类型-自定义文字', - builder: (_) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: const [ - TDAvatar( - size: TDAvatarSize.large, - type: TDAvatarType.customText, - text: 'A', - ), - TDAvatar( - size: TDAvatarSize.medium, - type: TDAvatarType.customText, - text: 'A', - ), - TDAvatar( - size: TDAvatarSize.small, - type: TDAvatarType.customText, - text: 'A', - ), - ], - ); - }), - ExampleItem( - desc: '特殊类型-带操作', - builder: (_) { - return Column( - mainAxisSize: MainAxisSize.max, - children: [ - TDAvatar( - size: TDAvatarSize.large, - type: TDAvatarType.operation, - avatarDisplayList: [ - imgUrl, - imgUrl, - imgUrl - ], - onTap: (){ - TDToast.showText('点击了操作', context: context); - } - ), - TDAvatar( - size: TDAvatarSize.medium, - type: TDAvatarType.operation, - avatarDisplayList: [ - imgUrl, - imgUrl, - imgUrl - ], - onTap: (){ - TDToast.showText('点击了操作', context: context); - } - ), - TDAvatar( - size: TDAvatarSize.small, - type: TDAvatarType.operation, - avatarDisplayList: [ - imgUrl, - imgUrl, - imgUrl - ], - onTap: (){ - TDToast.showText('点击了操作', context: context); - } - ), - ], - ); - }), - ExampleItem( - desc: '特殊类型-纯展示', - builder: (_) { - return Column( - mainAxisSize: MainAxisSize.max, - children: [ - TDAvatar( - size: TDAvatarSize.large, - type: TDAvatarType.display, - avatarDisplayList: [ - imgUrl, - imgUrl, - imgUrl - ], - displayText: '+5', - ), - TDAvatar( - size: TDAvatarSize.medium, - type: TDAvatarType.display, - avatarDisplayList: [ - imgUrl, - imgUrl, - imgUrl - ], - displayText: '+5', - ), - TDAvatar( - size: TDAvatarSize.small, - type: TDAvatarType.display, - avatarDisplayList: [ - imgUrl, - imgUrl, - imgUrl - ], - displayText: '+5', - ), - ], - ); - }), - ], - ); - } -} diff --git a/example/lib/tdesign/page/td_badge_page.dart b/example/lib/tdesign/page/td_badge_page.dart deleted file mode 100644 index 353719c81..000000000 --- a/example/lib/tdesign/page/td_badge_page.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; -import '../example_base.dart'; - - -class TDBadgePage extends StatefulWidget { - const TDBadgePage({Key? key}) : super(key: key); - - @override - State createState() => _TDBadgePageState(); -} - -class _TDBadgePageState extends State { - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '徽标 Badge', - children: [ - ExampleItem( - desc: '红点徽标', - builder: (_) { - return const TDBadge( - TDBadgeType.redPoint, - ); - }), - ExampleItem( - desc: '提醒徽标', - builder: (_) { - return const TDBadge( - TDBadgeType.remind, - ); - }), - ExampleItem( - desc: '消息徽标-个位数', - builder: (_) { - return const TDBadge( - TDBadgeType.message, - count: '2', - ); - }), - ExampleItem( - desc: '消息徽标-两位数', - builder: (_) { - return const TDBadge(TDBadgeType.message, count: '16'); - }), - ExampleItem( - desc: '消息徽标-三位数', - builder: (_) { - return const TDBadge(TDBadgeType.message, count: '128'); - }), - ExampleItem( - desc: '消息徽标-自定义内容', - builder: (_) { - return const TDBadge( - TDBadgeType.message, - message: '新消息提醒', - ); - }), - ExampleItem( - desc: '消息徽标-自定义内容-方形', - builder: (_) { - return const TDBadge( - TDBadgeType.message, - message: 'New', - border: TDBadgeBorder.small, - ); - }), - ExampleItem( - desc: '消息徽标-自定义内容-角标', - builder: (_) { - return Stack( - alignment: Alignment.topRight, - children: [ - Container( - padding: const EdgeInsets.only(left: 16), - alignment: Alignment.centerLeft, - child: TDText( - '单行标题', - textColor: TDTheme.of(context).fontGyColor1, - font: TDTheme.of(context).fontM, - ), - color: Colors.white, - height: 48, - width: MediaQuery.of(context).size.width, - ), - const TDBadge( - TDBadgeType.subscript, - message: 'NEW', - ), - ], - ); - }), - ], - ); - } -} diff --git a/example/lib/tdesign/page/td_bottom_nav_bar_page.dart b/example/lib/tdesign/page/td_bottom_nav_bar_page.dart deleted file mode 100644 index 79fabaf62..000000000 --- a/example/lib/tdesign/page/td_bottom_nav_bar_page.dart +++ /dev/null @@ -1,584 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - -class TDBottomNavBarPage extends StatelessWidget { - const TDBottomNavBarPage({Key? key}) : super(key: key); - - void onTapTab(BuildContext context, String tabName) { - TDToast.showText('点击了 $tabName', context: context); - } - - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '标签栏 TDBottomNavBar', - padding: const EdgeInsets.symmetric(vertical: 16), - children: [ - ExampleItem( - desc: '单层级纯文本标签栏', - builder: (BuildContext context) { - return TDBottomNavBar( - TDBottomNavBarType.text, - useVerticalDivider: true, - navigationTabs: [ - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏一', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏一', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - onTap: () { - onTapTab(context, '标签栏一'); - }), - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏二', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏二', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - onTap: () { - onTapTab(context, '标签栏二'); - }, - ), - ], - ); - }, - ), - ExampleItem( - desc: '单层级纯文本标签栏', - builder: (BuildContext context) { - return TDBottomNavBar( - TDBottomNavBarType.text, - useVerticalDivider: true, - navigationTabs: [ - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏一', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏一', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - onTap: () { - onTapTab(context, '标签栏一'); - }), - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏二', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏二', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - onTap: () { - onTapTab(context, '标签栏二'); - }, - ), - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏三', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏三', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - onTap: () { - onTapTab(context, '标签栏三'); - }, - ), - ], - ); - }, - ), - ExampleItem( - desc: '文本加图标标签栏', - builder: (BuildContext context) { - return TDBottomNavBar( - TDBottomNavBarType.iconText, - useVerticalDivider: true, - navigationTabs: [ - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏一', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏一', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 20, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 20, - ), - onTap: () { - onTapTab(context, '标签栏一'); - }, - ), - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏二', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏二', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 20, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 20, - ), - onTap: () { - onTapTab(context, '标签栏二'); - }), - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏三', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏三', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 20, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 20, - ), - onTap: () { - onTapTab(context, '标签栏三'); - }), - ], - ); - }, - ), - ExampleItem( - desc: '文本加图标标签栏', - builder: (BuildContext context) { - return TDBottomNavBar( - TDBottomNavBarType.iconText, - useVerticalDivider: true, - navigationTabs: [ - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏一', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏一', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 20, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 20, - ), - onTap: () { - onTapTab(context, '标签栏一'); - }, - ), - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏二', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏二', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 20, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 20, - ), - onTap: () { - onTapTab(context, '标签栏二'); - }), - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏三', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏三', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 20, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 20, - ), - onTap: () { - onTapTab(context, '标签栏三'); - }), - TDBottomNavBarTabConfig( - selectedText: Text( - '标签栏四', - style: TextStyle( - color: TDTheme.of(context).brandColor8, fontSize: 16), - ), - unselectedText: const Text( - '标签栏四', - style: TextStyle(color: Colors.grey, fontSize: 16), - ), - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 20, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 20, - ), - onTap: () { - onTapTab(context, '标签栏四'); - }), - ], - ); - }, - ), - ExampleItem( - desc: '纯图标标签栏', - builder: (BuildContext context) { - return TDBottomNavBar( - TDBottomNavBarType.icon, - useVerticalDivider: true, - navigationTabs: [ - TDBottomNavBarTabConfig( - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 30, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 30, - ), - onTap: () { - onTapTab(context, '第一个图标'); - }), - TDBottomNavBarTabConfig( - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 30, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 30, - ), - onTap: () { - onTapTab(context, '第二个图标'); - }, - ), - TDBottomNavBarTabConfig( - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 30, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 30, - ), - onTap: () { - onTapTab(context, '第三个图标'); - }), - TDBottomNavBarTabConfig( - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 30, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 30, - ), - onTap: () { - onTapTab(context, '第四个图标'); - }), - TDBottomNavBarTabConfig( - selectedIcon: Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 30, - ), - unselectedIcon: const Icon( - Icons.home, - color: Colors.grey, - size: 30, - ), - onTap: () { - onTapTab(context, '第五个图标'); - }), - ], - ); - }, - ), - ExampleItem( - desc: '底部导航栏自定义布局', - builder: (BuildContext context) { - return TDBottomNavBar( - TDBottomNavBarType.customLayout, - useVerticalDivider: true, - navigationTabs: [ - TDBottomNavBarTabConfig( - showBadge: true, - tdBadge: const TDBadge( - TDBadgeType.remind, - ), - badgeTopOffset: -2, - badgeRightOffset: -20, - selectWidget: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.home, - color: TDTheme.of(context).brandColor8, - size: 20, - ), - Text( - '首页', - style: TextStyle( - color: TDTheme.of(context).brandColor8, - fontSize: 16), - ) - ], - ), - unSelectWidget: Column( - mainAxisSize: MainAxisSize.min, - children: const [ - Icon( - Icons.home, - color: Colors.grey, - size: 20, - ), - Text( - '首页', - style: TextStyle(color: Colors.grey, fontSize: 16), - ) - ], - ), - onTap: () { - onTapTab(context, '首页'); - }, - ), - TDBottomNavBarTabConfig( - showBadge: true, - tdBadge: const TDBadge( - TDBadgeType.message, - message: 'New', - ), - badgeTopOffset: -2, - badgeRightOffset: -20, - selectWidget: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.category, - color: TDTheme.of(context).brandColor8, - size: 20, - ), - Text( - '分类', - style: TextStyle( - color: TDTheme.of(context).brandColor8, - fontSize: 16), - ) - ], - ), - unSelectWidget: Column( - mainAxisSize: MainAxisSize.min, - children: const [ - Icon( - Icons.category, - color: Colors.grey, - size: 20, - ), - Text( - '分类', - style: TextStyle(color: Colors.grey, fontSize: 16), - ) - ], - ), - onTap: () { - onTapTab(context, '分类'); - }, - ), - TDBottomNavBarTabConfig( - showBadge: true, - tdBadge: const TDBadge(TDBadgeType.message, count: '16'), - badgeTopOffset: -2, - badgeRightOffset: -10, - selectWidget: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Icon( - Icons.shopping_cart, - color: TDTheme.of(context).brandColor8, - size: 20, - ), - Text( - '购物车', - style: TextStyle( - color: TDTheme.of(context).brandColor8, - fontSize: 16), - ) - ], - ), - unSelectWidget: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: const [ - Icon( - Icons.shopping_cart, - color: Colors.grey, - size: 20, - ), - Text( - '购物车', - style: TextStyle(color: Colors.grey, fontSize: 16), - ) - ], - ), - onTap: () { - onTapTab(context, '购物车'); - }, - ), - TDBottomNavBarTabConfig( - showBadge: true, - tdBadge: const TDBadge(TDBadgeType.redPoint), - badgeTopOffset: -2, - badgeRightOffset: -10, - selectWidget: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.settings, - color: TDTheme.of(context).brandColor8, - size: 20, - ), - Text( - '展开', - style: TextStyle( - color: TDTheme.of(context).brandColor8, - fontSize: 16), - ) - ], - ), - unSelectWidget: Column( - mainAxisSize: MainAxisSize.min, - children: const [ - Icon( - Icons.settings, - color: Colors.grey, - size: 20, - ), - Text( - '展开', - style: TextStyle(color: Colors.grey, fontSize: 16), - ) - ], - ), - onTap: () { - onTapTab(context, '展开'); - }, - popUpButtonConfig: TDBottomNavBarPopUpBtnConfig( - popUpDialogConfig: TDBottomNavBarPopUpShapeConfig( - radius: 10, - arrowWidth: 16, - arrowHeight: 8, - ), - items: [ - '展开项一', - '展开项二', - '展开项三', - '展开项四', - '展开项五', - '展开项六', - '展开项七', - '展开项八', - ] - .map((e) => PopUpMenuItem( - value: e, - itemWidget: SizedBox( - //height: 30, - child: Text( - e, - style: TextStyle( - color: TDTheme.of(context).grayColor7, - fontSize: 16), - ), - ), - )) - .toList(), - onChanged: (v) { - TDToast.showText('点击了 $v', context: context); - })), - ], - ); - }, - ), - ], - ); - } -} diff --git a/example/lib/tdesign/page/td_button_page.dart b/example/lib/tdesign/page/td_button_page.dart deleted file mode 100644 index d03fcf2f5..000000000 --- a/example/lib/tdesign/page/td_button_page.dart +++ /dev/null @@ -1,313 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; -import '../example_base.dart'; - -class TDButtonPage extends StatefulWidget { - const TDButtonPage({Key? key}) : super(key: key); - - @override - State createState() => _TDButtonPageState(); -} - -class _TDButtonPageState extends State { - - void onTap() { - TDToast.showText('点击了按钮',context: context); - } - - void onLongPress() { - TDToast.showText('长按了按钮', context: context); - } - - @override - Widget build(BuildContext context) { - return Container( - color: TDTheme.of(context).grayColor2, - child: ExampleWidget( - title: '按钮 Button', - padding: const EdgeInsets.only(top: 8,bottom: 8), - children: [ - ExampleItem(desc: '可点击', builder: (context){ - return TDButton(content: '强按钮', - style: TDButtonStyle.primary(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '不可点击', builder: (context){ - return TDButton(content: '强按钮', - style: TDButtonStyle.primary(), - disabled: true, - onTap: onTap, - onLongPress: onLongPress,); - }), - - - ExampleItem(desc: '可点击', builder: (context){ - return TDButton(content: '弱按钮', - style: TDButtonStyle.weakly(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '不可点击', builder: (context){ - return TDButton(content: '弱按钮', - style: TDButtonStyle.weakly(), - disabled: true, - onTap: onTap, - onLongPress: onLongPress,); - }), - - - ExampleItem(desc: '可点击', builder: (context){ - return TDButton(content: '次按钮', - style: TDButtonStyle.secondary(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '不可点击', builder: (context){ - return TDButton(content: '次按钮', - style: TDButtonStyle.secondary(), - disabled: true, - onTap: onTap, - onLongPress: onLongPress,); - }), - - - ExampleItem(desc: '可点击', builder: (context){ - return TDButton(content: '带图标按钮', - style: TDButtonStyle.weakly(), - icon: TDIcons.app, - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '不可点击', builder: (context){ - return TDButton(content: '带图标按钮', - style: TDButtonStyle.weakly(), - icon: TDIcons.app, - disabled: true, - onTap: onTap, - onLongPress: onLongPress,); - }), - - ExampleItem(desc: '可点击', builder: (context){ - return TDButton(content: '强警告按钮', - style: TDButtonStyle.warningPrimary(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '不可点击', builder: (context){ - return TDButton(content: '强警告按钮', - style: TDButtonStyle.warningPrimary(), - disabled: true, - onTap: onTap, - onLongPress: onLongPress,); - }), - - - ExampleItem(desc: '可点击', builder: (context){ - return TDButton(content: '弱警告按钮', - style: TDButtonStyle.warningWeakly(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '不可点击', builder: (context){ - return TDButton(content: '弱警告按钮', - style: TDButtonStyle.warningWeakly(), - disabled: true, - onTap: onTap, - onLongPress: onLongPress,); - }), - - - ExampleItem(desc: '可点击', builder: (context){ - return Container( - width: MediaQuery.of(context).size.width, - padding: const EdgeInsets.all(10), - color: Colors.black12, - child: Stack( - alignment: Alignment.center, - children: [ - TDButton(content: '幽灵按钮', - style: TDButtonStyle.ghost(), - onTap: onTap, - onLongPress: onLongPress, - ) - ],), - ); - }), - ExampleItem(desc: '不可点击', builder: (context){ - return Container( - width: MediaQuery.of(context).size.width, - padding: const EdgeInsets.all(10), - color: Colors.black12, - child: Stack( - alignment: Alignment.center, - children: [ - TDButton(content: '幽灵按钮', - style: TDButtonStyle.ghost(), - // width: 343, - disabled: true, - onTap: onTap, - onLongPress: onLongPress,) - ],), - ); - }), - - - ExampleItem(desc: '可点击', builder: (context){ - return TDTextButton('文字按钮', - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '不可点击', builder: (context){ - return TDTextButton('文字按钮', - onTap: onTap, - onLongPress: onLongPress, - disabled: true, - ); - }), - - - ExampleItem(desc: '可点击', builder: (context){ - return TDButton(content: '通栏按钮', - style: TDButtonStyle.full(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '不可点击', builder: (context){ - return TDButton(content: '通栏按钮', - style: TDButtonStyle.full(), - disabled: true, - onTap: onTap, - onLongPress: onLongPress,); - }), - - - ExampleItem(desc: '两个按钮', builder: (context){ - return Row( - children: [ - Expanded(child: TDButton(content: '次按钮', - style: TDButtonStyle.fullSecondary(), - onTap: onTap, - onLongPress: onLongPress, - )), - Expanded(child: TDButton(content: '主按钮', - style: TDButtonStyle.full(), - onTap: onTap, - onLongPress: onLongPress, - )), - ], - ); - }), - - - - - - ExampleItem(desc: '尺寸', builder: (context){ - return SizedBox( - height: 50, - child: TDText( - '下方是尺寸示例', - font: TDTheme.of().fontL, - textColor: TDTheme.of().warningNormalColor, - ), - ); - }), - - - ExampleItem(desc: '大', builder: (context){ - return TDButton(content: '强按钮', - style: TDButtonStyle.primary(), - size: TDButtonSize.large, - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '中(默认)', builder: (context){ - return TDButton(content: '强按钮', - size: TDButtonSize.medium, - style: TDButtonStyle.primary(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '小(宽度自适应)', builder: (context){ - return TDButton(content: '强按钮', - size: TDButtonSize.small, - style: TDButtonStyle.primary(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '小(宽度自适应)', builder: (context){ - return TDButton(content: '弱按钮', - size: TDButtonSize.small, - style: TDButtonStyle.weakly(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '小(宽度自适应)', builder: (context){ - return TDButton(content: '次按钮', - size: TDButtonSize.small, - style: TDButtonStyle.secondary(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '小(宽度自适应)', builder: (context){ - return TDButton(content: '带图标按钮', - size: TDButtonSize.small, - style: TDButtonStyle.weakly(), - icon: TDIcons.app, - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '小(宽度自适应)', builder: (context){ - return TDButton(content: '强警告按钮', - size: TDButtonSize.small, - style: TDButtonStyle.warningPrimary(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '小(宽度自适应)', builder: (context){ - return TDButton(content: '弱警告按钮', - size: TDButtonSize.small, - style: TDButtonStyle.warningWeakly(), - onTap: onTap, - onLongPress: onLongPress, - ); - }), - ExampleItem(desc: '小(宽度自适应)', builder: (context){ - return Container( - width: 200, - padding: const EdgeInsets.all(10), - color: Colors.black12, - child: Stack( - alignment: Alignment.center, - children: [ - TDButton(content: '幽灵按钮', - size: TDButtonSize.small, - style: TDButtonStyle.ghost(), - onTap: onTap, - onLongPress: onLongPress, - ) - ],), - ); - }), - - - ]), - ); - } -} diff --git a/example/lib/tdesign/page/td_checkbox_page.dart b/example/lib/tdesign/page/td_checkbox_page.dart deleted file mode 100644 index d7540d2cc..000000000 --- a/example/lib/tdesign/page/td_checkbox_page.dart +++ /dev/null @@ -1,197 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - -/// -/// TDCheckbox演示 -/// -class TDCheckboxPage extends StatefulWidget { - const TDCheckboxPage({Key? key}) : super(key: key); - - @override - State createState() { - return TDCheckboxPageState(); - } -} - -class TDCheckboxPageState extends State { - - List? checkIds; - - TDCheckboxGroupController? controller; - - @override - void initState() { - super.initState(); - controller = TDCheckboxGroupController(); - } - - @override - Widget build(BuildContext context) { - const itemCount = 4; - Widget getAllIcon(bool checked, bool halfSelected) { - return Icon( - checked ? TDIcons.check_circle_filled : halfSelected ? TDIcons.minus_circle_filled : TDIcons.circle, - size: 24, - color: (checked || halfSelected) ? TDTheme.of(context).brandColor8 : TDTheme.of(context).grayColor4 - ); - } - - return ExampleWidget( - title: '多选框 Checkbox', - children: [ - ExampleItem(desc: '基础多选框', builder: (context){ - return Column( - mainAxisSize: MainAxisSize.min, - children: const [ - TDCheckbox( - title: '多选', - checked: true, - ), - TDCheckbox( - title: '多选', - ), - TDCheckbox( - title: '禁用状态', - enable: false, - checked: true, - ), - TDCheckbox( - title: '多选多选多选多选多选多选多选多选多选多选多选多选', - titleMaxLine: 2, - subTitleMaxLine: 2, - enable: false, - ), - TDCheckbox( - title: '多选', - subTitle: '单选单选单选单选单选单选单选单选单选单选单选单选', - titleMaxLine: 2, - subTitleMaxLine: 2, - ), - ], - ); - }), - ExampleItem(desc: '右侧多选框', builder: (context){ - return Column( - mainAxisSize: MainAxisSize.min, - children: const [ - TDCheckbox( - contentDirection: TDContentDirection.left, - title: '多选', - checked: true, - ), - TDCheckbox( - contentDirection: TDContentDirection.left, - title: '多选', - checked: true, - ), - TDCheckbox( - contentDirection: TDContentDirection.left, - title: '多选多选多选多选多选多选多选多选多选多选多选多选多选', - ), - TDCheckbox( - contentDirection: TDContentDirection.left, - title: '多选', - subTitle: '单选单选单选单选单选单选单选单选单选单选单选单选', - ), - ], - ); - }), - ExampleItem(desc: '自定义图标多框类型', builder: (context){ - return Column( - mainAxisSize: MainAxisSize.min, - children: const [ - TDCheckbox( - style: TDCheckboxStyle.square, - title: '多选', - ), - TDCheckbox( - style: TDCheckboxStyle.square, - title: '多选', - checked: true, - ), - TDCheckbox( - style: TDCheckboxStyle.square, - title: '多选', - checked: true, - ), - ], - ); - }), - ExampleItem(desc: '规格', builder: (context){ - return Column( - mainAxisSize: MainAxisSize.min, - children: const [ - TDCheckbox( - title: '多选 H48', - checked: true, - ), - TDCheckbox( - title: '多选 H56', - size: TDCheckBoxSize.large, - ), - ], - ); - }), - ExampleItem(desc: '带全选多选框', builder: (context){ - return TDCheckboxGroup( - controller: controller, - onChangeGroup: (checkedId) { - checkIds = checkedId; - setState(() {}); - }, - checkedIds: checkIds, - onOverloadChecked: () { - TDToast.showText('最大只能勾选3个选项', context: context); - }, - child: ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: itemCount, - itemBuilder: (BuildContext context, int index) { - if(index == 0) { - return TDCheckbox( - contentDirection: TDContentDirection.right, - title: '全选', - id: '0', - customIconBuilder: (context, checked) { - var length = controller!.allChecked().length - (controller!.checked('0') ? 1 : 0); - var allCheck = itemCount - 1 == length; - var halfSelected = - controller != null - && !allCheck - && length > 0; - return getAllIcon(allCheck, halfSelected); - }, - onCheckBoxChanged: (checked){ - if (checked) { - controller?.toggleAll(true); - } else { - controller?.toggleAll(false); - } - }, - ); - } - return TDCheckbox( - contentDirection: TDContentDirection.right, - title: '多选${index}', - id: '$index', - onCheckBoxChanged: (checked) { - var length = controller!.allChecked().length - (controller!.checked('0') ? 1 : 0); - var allCheck = itemCount - 1 == length; - var halfSelected = - controller != null - && !allCheck - && length > 0; - controller!.toggle('0', allCheck); - getAllIcon(allCheck, halfSelected); - }, - ); - }, - )); - }) - ] - ); - } -} diff --git a/example/lib/tdesign/page/td_date_picker_page.dart b/example/lib/tdesign/page/td_date_picker_page.dart deleted file mode 100644 index a07fb32a7..000000000 --- a/example/lib/tdesign/page/td_date_picker_page.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - - -class TDDatePickerPage extends StatefulWidget { - const TDDatePickerPage({Key? key}) : super(key: key); - - @override - State createState() => _TDDatePickerPageState(); -} - -class _TDDatePickerPageState extends State { - String selected_1 = ''; - String selected_2 = ''; - String selected_3 = ''; - String selected_4 = ''; - String selected_5 = ''; - String selected_6 = ''; - - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '时间选择器 DatePicker', - children: [ - ExampleItem(desc: '年月日', builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '选择日期', - onTap: () => TDPicker.showDatePicker(context, title: '选择时间', - onConfirm: (selected) { - setState(() { - selected_1 = '日期: $selected'; - }); - }, - dateStart: [2010, 12, 20], - dateEnd: [2022, 2, 28], - initialDate: [2012, 1, 1]), - ), - TDText( - selected_1, - ), - ]); - }), - ExampleItem(desc: '年月', builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '选择日期', - onTap: () => TDPicker.showDatePicker(context, title: '选择时间', - onConfirm: (selected) { - setState(() { - selected_2 = '日期: $selected'; - }); - }, - useDay: false, - dateStart: [2010, 12, 20], - dateEnd: [2022, 2, 28], - initialDate: [2012, 1, 1]), - ), - TDText( - selected_2, - ), - ]); - }), - ExampleItem(desc: '月日', builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '选择日期', - onTap: () => TDPicker.showDatePicker(context, title: '选择时间', - onConfirm: (selected) { - setState(() { - selected_3 = '日期: $selected'; - }); - }, - useYear: false, - dateStart: [2010, 12, 20], - dateEnd: [2022, 2, 28], - initialDate: [2012, 1, 1]), - ), - TDText( - selected_3, - ), - ]); - }), - ExampleItem(desc: '时分秒', builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '选择日期', - onTap: () => TDPicker.showDatePicker(context, title: '选择时间', - onConfirm: (selected) { - setState(() { - selected_4 = '日期: $selected'; - }); - }, - useYear: false, - useMonth: false, - useDay: false, - useHour: true, - useMinute: true, - useSecond: true, - dateStart: [2010, 12, 20], - dateEnd: [2022, 2, 28], - initialDate: [2012, 1, 1]), - ), - TDText( - selected_4, - ), - ]); - }), - ExampleItem(desc: '年月日时分秒', builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '选择日期', - onTap: () => TDPicker.showDatePicker(context, title: '选择时间', - onConfirm: (selected) { - setState(() { - selected_5 = '日期: $selected'; - }); - }, - useHour: true, - useMinute: true, - useSecond: true, - dateStart: [2010, 12, 20], - dateEnd: [2022, 2, 28], - initialDate: [2012, 1, 1]), - ), - TDText( - selected_5, - ), - ]); - }), - ], - ); - } -} diff --git a/example/lib/tdesign/page/td_dialog_page.dart b/example/lib/tdesign/page/td_dialog_page.dart deleted file mode 100644 index 2ff1bc984..000000000 --- a/example/lib/tdesign/page/td_dialog_page.dart +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Created by haozhicao@tencent.com on 6/17/22. - * td_dialog_page.dart - * - */ - -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - - -class TDDialogPage extends StatefulWidget { - const TDDialogPage({Key? key}) : super(key: key); - - @override - State createState() => _TDDialogPageState(); -} - -class _TDDialogPageState extends State { - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '对话框 Dialog', - padding: const EdgeInsets.all(3), - children: [ - _dividerWidget('确认弹框'), - ElevatedButton( - onPressed: () { - _showDialog(const TDConfirmDialog( - title: '对话框标题', - )); - }, - child: const Text('只有标题')), - ElevatedButton( - onPressed: () { - _showDialog(const TDConfirmDialog( - title: '对话框标题长,对话框标题长,对话框标题长长长长长长长长', - )); - }, - child: const Text('长标题')), - ElevatedButton( - onPressed: () { - _showDialog(const TDConfirmDialog( - content: - '告知当前状态、信息和解决方法,等内容。描述文案尽可能控制在三行内,告知当前状态、信息和解决方法,等内容。描述文案尽可能控制在三行内告知当前状态、信息和解决方法,等内容。描述文案尽可能控制在三行内', - )); - }, - child: const Text('只有内容')), - ElevatedButton( - onPressed: () { - _showDialog(TDConfirmDialog( - title: '对话框标题', - content: - '告知当前状态、信息和解决方法,等内容。描述文案尽可能控制在三行内,告知当前状态、信息和解决方法,等内容。', - action: () { - print('知道了'); - }, - )); - }, - child: const Text('标题+内容')), - ElevatedButton( - onPressed: () { - _showDialog(const TDConfirmDialog( - contentMaxHeight: 100, - title: '对话框标题', - content: - '告知当前状态、信息和解决方法,等内容。描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很多描述文案很', - )); - }, - child: const Text('标题+可滚动内容')), - _dividerWidget('多选项弹窗'), - ElevatedButton( - onPressed: () { - _showDialog(TDAlertDialog( - title: '对话框标题', - content: - '告知当前状态、信息和解决方法,等内容。描述文案尽可能控制在三行内,告知当前状态、信息和解决方法,等内容。', - rightBtn: TDDialogButton( - title: '确定', - action: () { - print('点击了确定按钮'); - }), - )); - }, - child: const Text('左右选择')), - ElevatedButton( - onPressed: () { - _showDialog(TDAlertDialog.vertical( - title: '对话框标题', - content: - '告知当前状态、信息和解决方法,等内容。描述文案尽可能控制在三行内,告知当前状态、信息和解决方法,等内容。', - buttons: [ - TDDialogButton( - title: '第一行', - action: () { - print('点击了第一行'); - }), - TDDialogButton( - title: '第二行', - action: () { - print('点击了第二行'); - }), - TDDialogButton( - title: '取消', - action: () { - print('点击了取消'); - }, - titleColor: Colors.black87, - fontWeight: FontWeight.w400) - ], - )); - }, - child: const Text('上下选择')), - _dividerWidget('输入类弹窗'), - ElevatedButton( - onPressed: () { - _showDialog(TDInputDialog( - title: '对话框标题', - content: '告知当前状态、信息和解决方法', - textEditingController: TextEditingController(), - rightBtn: TDDialogButton( - title: '确定', - action: () { - print('点击了确定按钮'); - }), - )); - }, - child: const Text('输入类对话框')), - _dividerWidget('带图片弹窗'), - ElevatedButton( - onPressed: () { - _showDialog(TDImageDialog( - title: '对话框标题', - content: '告知当前状态、信息和解决方法', - image: Image.network( - 'http://static.runoob.com/images/demo/demo2.jpg', - // fit: BoxFit.cover, - ), - rightBtn: TDDialogButton( - title: '确定', - action: () { - print('点击了确定按钮'); - }), - )); - }, - child: const Text('带图片弹窗')), - ]); - } - - void _showDialog(Widget dialog,{bool useRootNavigator = false}) { - showGeneralDialog( - context: context, - pageBuilder: (BuildContext buildContext, Animation animation, - Animation secondaryAnimation) { - return dialog; - }, - useRootNavigator: useRootNavigator, - barrierDismissible: true, - barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, - barrierColor: Colors.black54, - transitionDuration: const Duration(milliseconds: 100), - ); - } - - Widget _dividerWidget(String title) { - return Row( - children: [ - const Spacer(), - const TDDivider( - height: 1, - width: 100, - ), - const SizedBox( - width: 10, - ), - TDText(title), - const SizedBox( - width: 10, - ), - const TDDivider( - height: 1, - width: 100, - ), - const Spacer(), - ], - ); - } -} diff --git a/example/lib/tdesign/page/td_divider_page.dart b/example/lib/tdesign/page/td_divider_page.dart deleted file mode 100644 index 510a73858..000000000 --- a/example/lib/tdesign/page/td_divider_page.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - -class TDDividerPage extends StatelessWidget { - const TDDividerPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '分割线 Divider', - padding: const EdgeInsets.only(top: 16, bottom: 16), - children: [ - ExampleItem(desc: '直线拉通', builder: (_){ - return const TDDivider(); - }), - ExampleItem(desc: '虚线拉通', builder: (_){ - return const TDDivider(isDashed: true,); - }), - ExampleItem(desc: '左右间距', builder: (_){ - return const TDDivider(margin: EdgeInsets.only(left: 16, right: 16),); - }), - ExampleItem(desc: '左侧拉通', builder: (_){ - return const TDDivider(margin: EdgeInsets.only(left: 16, ),); - }), - ExampleItem(desc: '自定义左侧间距', builder: (_){ - return const TDDivider(margin: EdgeInsets.only(left: 76, ),); - }), - ExampleItem(desc: '左右间距(虚线)', builder: (_){ - return const TDDivider(margin: EdgeInsets.only(left: 16, right: 16),isDashed: true,); - }), - ExampleItem(desc: '自定义左侧间距(虚线)', builder: (_){ - return const TDDivider(margin: EdgeInsets.only(left: 76, ),isDashed: true,); - }), - ExampleItem(desc: '垂直分割', builder: (_){ - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - TDText('文字信息', textColor: TDTheme.of(context).fontGyColor3,), - const TDDivider(width: 0.5, height: 12, margin: EdgeInsets.only(left: 8, right: 8),), - TDText('文字信息', textColor: TDTheme.of(context).fontGyColor3), - const TDDivider(width: 0.5, height: 12, margin: EdgeInsets.only(left: 8, right: 8),isDashed: true, direction: Axis.vertical,), - TDText('文字信息', textColor: TDTheme.of(context).fontGyColor3), - ], - ); - }), - ExampleItem(desc: '文字+直线', builder: (_){ - return const TDDivider(margin: EdgeInsets.only(left: 16, right: 16), text: '文字信息',); - }), - ExampleItem(desc: '文字+虚线', builder: (_){ - return const TDDivider(margin: EdgeInsets.only(left: 16, right: 16), text: '文字信息',isDashed: true,); - }), - ExampleItem(desc: '纯文字', builder: (_){ - return const TDDivider(hideLine: true, text: '文字信息',); - }), - ]); - } -} - - diff --git a/example/lib/tdesign/page/td_empty_page.dart b/example/lib/tdesign/page/td_empty_page.dart deleted file mode 100644 index 31c30f658..000000000 --- a/example/lib/tdesign/page/td_empty_page.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; -import 'package:tdesign_flutter_example/tdesign/example_base.dart'; - - -class TDEmptyPage extends StatefulWidget { - const TDEmptyPage({Key? key}) : super(key: key); - - @override - State createState() => _TDEmptyPageState(); -} - -class _TDEmptyPageState extends State { - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '空状态 Empty', - children: [ - Container( - height: 720, - color: Colors.white, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDEmpty( - type: TDEmptyType.operation, - operationText: '操作按钮', - emptyText: '描述文字', - image: Icon( - TDIcons.info_circle_filled, - size: 84, - color: TDTheme.of(context).fontGyColor3, - ), - ), - ], - ), - ) - ]); - } -} diff --git a/example/lib/tdesign/page/td_icon_page.dart b/example/lib/tdesign/page/td_icon_page.dart deleted file mode 100644 index 9474a8acf..000000000 --- a/example/lib/tdesign/page/td_icon_page.dart +++ /dev/null @@ -1,41 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - - -class TDIconPage extends StatefulWidget { - const TDIconPage({Key? key}) : super(key: key); - - @override - State createState() => _TDIconPageState(); -} - -class _TDIconPageState extends State { - @override - Widget build(BuildContext context) { - return ExampleWidget(title: 'icon图标', - children: [ - Container( - color: Colors.white, - alignment: Alignment.center, - child: Wrap( - children: [ - for (var iconData in TDIcons.all.values) Container( - height: 100, - width: 175, - - child: Column( - children: [ - Icon(iconData), - TDText(iconData.name) - ], - ), - ) - ], - ), - ) - ]); - - } -} diff --git a/example/lib/tdesign/page/td_image_page.dart b/example/lib/tdesign/page/td_image_page.dart deleted file mode 100644 index 9fc15fb8b..000000000 --- a/example/lib/tdesign/page/td_image_page.dart +++ /dev/null @@ -1,212 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - -class TDImagePage extends StatefulWidget { - const TDImagePage({Key? key}) : super(key: key); - @override - State createState() => TDImageState(); -} - -class TDImageState extends State with SingleTickerProviderStateMixin { - late Animation animation; - late AnimationController animationController; - - @override - void initState() { - super.initState(); - animationController = - AnimationController(vsync: this, duration: const Duration(seconds: 4)); - animation = Tween(begin: 0.0, end: 4.0).animate(animationController); - animationController.repeat(); - } - - @override - void dispose() { - animationController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - var loadingUrl = 'https://images7.alphacoders.com/691/thumbbig-691004.webp'; - var url = 'http://www.zmaomao.com/uploads/201204/11-20120416014B36.jpg'; - return ExampleWidget( - title: '图片 Image', - children: [ - ExampleItem( - desc: '状态-加载默认', - builder: (_) { - return TDImage( - loadingUrl, - type: TDImageType.roundedSquare, - ); - } - ), - ExampleItem( - desc: '状态-加载自定义', - builder: (_) { - return TDImage( - loadingUrl, - loadingWidget: RotationTransition( - turns: animation, - alignment: Alignment.center, - child: const Icon(TDIcons.loading, size: 22,) - ), - type: TDImageType.roundedSquare, - ); - } - ), - ExampleItem( - desc: '状态-失败默认', - builder: (_) { - return const TDImage( - 'error', - type: TDImageType.roundedSquare, - ); - } - ), - ExampleItem( - desc: '状态-失败自定义', - builder: (_) { - return TDImage( - 'error', - errorWidget: TDText( - '加载失败', - forceVerticalCenter: true, - font: TDTheme.of(context).fontXXS, - fontWeight: FontWeight.w500, - textColor: TDTheme - .of(context) - .fontGyColor3, - ), - type: TDImageType.roundedSquare, - ); - } - ), - ExampleItem( - desc: '类型-裁剪', - builder: (_) { - return TDImage( - url, - type: TDImageType.clip, - ); - } - ), - ExampleItem( - desc: '类型-适应高', - builder: (_) { - return TDImage( - url, - width: 89, - height: 72, - type: TDImageType.fitHeight, - ); - } - ), - ExampleItem( - desc: '类型-拉伸', - builder: (_) { - return TDImage( - url, - width: 134, - height: 72, - type: TDImageType.stretch, - ); - } - ), - ExampleItem( - desc: '类型-方形', - builder: (_) { - return TDImage( - url, - type: TDImageType.square, - ); - } - ), - ExampleItem( - desc: '类型-圆角方形', - builder: (_) { - return TDImage( - url, - type: TDImageType.roundedSquare, - ); - } - ), - ExampleItem( - desc: '类型-圆形', - builder: (_) { - return TDImage( - url, - type: TDImageType.circle, - ); - } - ), - ExampleItem( - desc: '规格-图片120', - builder: (_) { - return TDImage( - url, - size: TDImageSize.xl, - type: TDImageType.roundedSquare, - ); - } - ), - ExampleItem( - desc: '规格-图片72', - builder: (_) { - return TDImage( - url, - size: TDImageSize.l, - type: TDImageType.roundedSquare, - ); - } - ), - ExampleItem( - desc: '规格-图片56', - builder: (_) { - return TDImage( - url, - size: TDImageSize.m, - type: TDImageType.roundedSquare, - ); - } - ), - ExampleItem( - desc: '规格-图片48', - builder: (_) { - return TDImage( - url, - size: TDImageSize.s, - type: TDImageType.roundedSquare, - ); - } - ), - ExampleItem( - desc: '规格-图片32', - builder: (_) { - return TDImage( - url, - size: TDImageSize.xs, - type: TDImageType.roundedSquare, - ); - } - ), - ExampleItem( - desc: '规格-图片24', - builder: (_) { - return TDImage( - url, - size: TDImageSize.xxs, - type: TDImageType.roundedSquare, - ); - } - ), - - ], - ); - } -} - - diff --git a/example/lib/tdesign/page/td_input_page.dart b/example/lib/tdesign/page/td_input_page.dart deleted file mode 100644 index 987bfcba4..000000000 --- a/example/lib/tdesign/page/td_input_page.dart +++ /dev/null @@ -1,238 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - -class TDInputViewPage extends StatefulWidget { - const TDInputViewPage({Key? key}) : super(key: key); - - @override - _TDInputViewPageState createState() => _TDInputViewPageState(); -} - -class _TDInputViewPageState extends State { - String inputText = '请输入...'; - var controller = TextEditingController(); - - @override - Widget build(BuildContext context) { - return ExampleWidget( - backgroundColor: const Color(0xFFF0F2F5), - title: '输入框 Input', - children: [ - ExampleItem( - desc: '基础文本框', - builder: (_) { - return TDInput( - leftLabel: '标签文字', - controller: controller, - backgroundColor: Colors.white, - hintText: '请输入文字', - ); - }), - ExampleItem( - desc: '无标题文本框', - builder: (_) { - return TDInput( - controller: controller, - backgroundColor: Colors.white, - hintText: '请输入文字', - ); - }), - ExampleItem( - desc: '带提示信息文本框', - builder: (_) { - return TDInput( - leftLabel: '标签文字', - controller: controller, - backgroundColor: Colors.white, - hintText: '请输入文字', - rightBtn: Icon( - TDIcons.error_circle_filled, - color: TDTheme.of(context).fontGyColor3, - ), - ); - }), - ExampleItem( - desc: '两行样式文本框', - builder: (_) { - return TDInput( - leftLabel: '标签文字', - type: TDInputType.twoLine, - controller: controller, - backgroundColor: Colors.white, - hintText: '请输入文字', - ); - }), - ExampleItem( - desc: '长标题文本框', - builder: (_) { - return TDInput( - leftLabel: '超长需换行的标签', - controller: controller, - backgroundColor: Colors.white, - hintText: '请输入文字', - ); - }), - ExampleItem( - desc: '长文本输入文本框-无标题', - builder: (_) { - return TDInput( - type: TDInputType.longText, - controller: controller, - backgroundColor: Colors.white, - hintText: '请输入文字', - maxLines: 8, - ); - }), - ExampleItem( - desc: '长文本输入文本框', - builder: (_) { - return TDInput( - type: TDInputType.longText, - leftLabel: '标签文字', - controller: controller, - backgroundColor: Colors.white, - hintText: '请输入文字', - maxLines: 8, - ); - }), - ExampleItem( - desc: '状态--不可编辑文字', - builder: (_) { - return TDInput( - type: TDInputType.normal, - leftLabel: '标签文字', - readOnly: true, - controller: controller, - hintText: '不可编辑文字', - hintTextStyle: TextStyle(color: TDTheme.of(context).fontGyColor1), - backgroundColor: Colors.white, - ); - }), - ExampleItem( - desc: '状态--一段错误填写的内容', - builder: (_) { - return TDInput( - type: TDInputType.normal, - leftLabel: '标签文字', - readOnly: true, - controller: controller, - hintText: '一段错误填写的内容', - errorText: '提示信息', - hintTextStyle: TextStyle(color: TDTheme.of(context).errorColor6), - backgroundColor: Colors.white, - ); - }), - ExampleItem( - desc: '小规格H48', - builder: (_) { - return TDInput( - type: TDInputType.normal, - leftLabel: '小规格H48', - controller: controller, - hintText: '请填写内容', - backgroundColor: Colors.white, - ); - }), - ExampleItem( - desc: '大规格H56', - builder: (_) { - return TDInput( - size: TDInputSize.large, - type: TDInputType.normal, - leftLabel: '大规格H56', - controller: controller, - hintText: '请填写内容', - backgroundColor: Colors.white, - ); - }), - ExampleItem( - desc: '特殊类型', - builder: (_) { - return TDInput( - type: TDInputType.normal, - controller: controller, - hintText: '请输入手机号码', - backgroundColor: Colors.white, - rightBtn: Row( - children: [ - Container(width: 0.5, height: 24, color: TDTheme.of(context).grayColor3,), - const SizedBox(width: 16,), - TDText('发送验证码', textColor: TDTheme.of(context).brandColor8), - ], - ), - onTapBtn: (){ - TDToast.showText('点击了发送验证码', context: context); - }, - ); - }), - ExampleItem( - desc: '特殊类型', - builder: (_) { - return TDInput( - type: TDInputType.normal, - size: TDInputSize.small, - controller: controller, - leftLabel: '验证码', - hintText: '输入验证码', - backgroundColor: Colors.white, - rightBtn: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container(width: 0.5, height: 36, color: TDTheme.of(context).grayColor3,), - const SizedBox(width: 16,), - Image.network('https://img2018.cnblogs.com/blog/736399/202001/736399-20200108170302307-1377487770.jpg', width: 72, height: 36,) - ], - ), - onTapBtn: (){ - TDToast.showText('点击了发送验证码', context: context); - }, - ); - }), - ExampleItem( - desc: '特殊类型', - builder: (_) { - return TDInput( - type: TDInputType.normal, - controller: controller, - obscureText: true, - leftLabel: '输入密码', - hintText: '请输入密码', - backgroundColor: Colors.white, - rightBtn: Icon(TDIcons.close_circle_filled, color: TDTheme.of(context).fontGyColor3,), - onTapBtn: (){ - controller.clear(); - }, - ); - }), - ExampleItem( - desc: '特殊类型', - builder: (_) { - return TDInput( - type: TDInputType.special, - controller: controller, - leftLabel: '价格', - hintText: '0.00', - backgroundColor: Colors.white, - textAlign: TextAlign.end, - rightWidget: TDText('元', textColor: TDTheme.of(context).fontGyColor1), - ); - }), - ExampleItem( - desc: '特殊类型', - builder: (_) { - return TDInput( - type: TDInputType.special, - controller: controller, - leftLabel: '数量', - hintText: '输入数量', - backgroundColor: Colors.white, - textAlign: TextAlign.end, - rightWidget: TDText('个', textColor: TDTheme.of(context).fontGyColor1), - ); - }), - ], - ); - } -} diff --git a/example/lib/tdesign/page/td_loading_page.dart b/example/lib/tdesign/page/td_loading_page.dart deleted file mode 100644 index 97f44a24c..000000000 --- a/example/lib/tdesign/page/td_loading_page.dart +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Created by haozhicao@tencent.com on 6/28/22. - * td_loading_page.dart - * - */ - -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; -import '../example_base.dart'; - -class TDLoadingPage extends StatefulWidget { - - const TDLoadingPage({Key? key}):super(key: key); - - @override - State createState() => _TDLoadingPageState(); -} - -class _TDLoadingPageState extends State { - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '加载 Loading', - children: [ - - TDText('类型', font: TDTheme.of(context).fontXL,), - ExampleItem(desc: '纯图标', builder: (_){ - return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.circle, - ), - const TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.activity, - ), - TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.point, - iconColor: TDTheme.of(context).brandColor8, - ), - ], - ); - }), - ExampleItem(desc: '图标加文字横向', builder: (_){ - return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.circle, - text: '加载中…', - axis: Axis.horizontal, - ), - const TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.activity, - text: '加载中…', - axis: Axis.horizontal, - ), - TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.circle, - text: '加载中…', - axis: Axis.horizontal, - textColor: TDTheme.of(context).brandHoverColor, - ), - ], - ); - }), - ExampleItem(desc: '图标加文字竖向', builder: (_){ - return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.circle, - text: '加载中…', - axis: Axis.vertical, - ), - const TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.activity, - text: '加载中…', - axis: Axis.vertical, - ), - TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.circle, - text: '加载中…', - axis: Axis.vertical, - textColor: TDTheme.of(context).brandHoverColor, - ), - ], - ); - }), - ExampleItem(desc: '纯文字', builder: (_){ - // TODO: 加载失败和刷新 - return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: const [ - TDLoading( - size: TDLoadingSize.medium, - text: '加载中…', - ), - ], - ); - }), - - TDText('规格', font: TDTheme.of(context).fontXL,), - - ExampleItem(desc: '图标加文字横向-circle', builder: (_){ - return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: const [ - TDLoading( - size: TDLoadingSize.large, - icon: TDLoadingIcon.circle, - text: '加载中(大)…', - axis: Axis.horizontal, - ), - TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.circle, - text: '加载中(中)…', - axis: Axis.horizontal, - ), - TDLoading( - size: TDLoadingSize.small, - icon: TDLoadingIcon.circle, - text: '加载中(小)…', - axis: Axis.horizontal, - ), - ], - ); - }), - ExampleItem(desc: '图标加文字横向-activity', builder: (_){ - return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - TDLoading( - size: TDLoadingSize.large, - icon: TDLoadingIcon.activity, - text: '加载中(大)…', - iconColor: TDTheme.of(context).brandColor8 , - axis: Axis.horizontal, - ), - TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.activity, - text: '加载中(中)…', - iconColor: TDTheme.of(context).brandColor8 , - axis: Axis.horizontal, - ), - TDLoading( - size: TDLoadingSize.small, - icon: TDLoadingIcon.activity, - text: '加载中(小)…', - iconColor: TDTheme.of(context).brandColor8 , - axis: Axis.horizontal, - ), - ], - ); - }), - - ExampleItem(desc: '图标加文字竖向', builder: (_){ - return Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: const [ - TDLoading( - size: TDLoadingSize.large, - icon: TDLoadingIcon.circle, - text: '加载中(大)…', - ), - TDLoading( - size: TDLoadingSize.medium, - icon: TDLoadingIcon.circle, - text: '加载中(中)…', - ), - TDLoading( - size: TDLoadingSize.small, - icon: TDLoadingIcon.circle, - text: '加载中(小)…', - ), - ], - ); - }), - ]); - } -} diff --git a/example/lib/tdesign/page/td_navbar_page.dart b/example/lib/tdesign/page/td_navbar_page.dart deleted file mode 100644 index edf921739..000000000 --- a/example/lib/tdesign/page/td_navbar_page.dart +++ /dev/null @@ -1,155 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; -import '../example_base.dart'; - -class TDNavBarPage extends StatelessWidget { - const TDNavBarPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '导航栏 NavBar', - children: [ - ExampleItem( - desc: '基础导航栏', - builder: (_) { - return const TDNavBar(title: '标题'); - }, - ), - ExampleItem( - desc: '左侧双操作导航栏', - builder: (_) { - return TDNavBar( - title: '标题', - useDefaultBack: false, - leftBarItems: [ - TDNavBarItem(icon: TDIcons.chevron_left), - TDNavBarItem(icon: TDIcons.close), - ], - rightBarItems: [TDNavBarItem(icon: TDIcons.ellipsis)], - ); - }, - ), - ExampleItem( - desc: '右侧双操作导航栏', - builder: (_) { - return TDNavBar( - title: '标题', - rightBarItems: [ - TDNavBarItem(icon: TDIcons.notification), - TDNavBarItem(icon: TDIcons.ellipsis), - ], - ); - }, - ), - ExampleItem( - desc: '基础小程序导航栏', - builder: (_) { - return TDNavBar( - title: '标题', - useDefaultBack: false, - useBorderStyle: true, - rightBarItems: [ - TDNavBarItem(icon: TDIcons.ellipsis), - TDNavBarItem(icon: TDIcons.add_circle), - ], - ); - }, - ), - ExampleItem( - desc: '带返回导航栏', - builder: (_) { - return TDNavBar( - title: '标题', - useBorderStyle: true, - rightBarItems: [ - TDNavBarItem(icon: TDIcons.ellipsis), - TDNavBarItem(icon: TDIcons.add_circle), - ], - ); - }, - ), - ExampleItem( - desc: '带返回、主页按钮导航栏', - builder: (_) { - return TDNavBar( - title: '标题', - useDefaultBack: false, - useBorderStyle: true, - leftBarItems: [ - TDNavBarItem(icon: TDIcons.chevron_left), - TDNavBarItem(icon: TDIcons.home), - ], - rightBarItems: [ - TDNavBarItem(icon: TDIcons.ellipsis), - TDNavBarItem(icon: TDIcons.add_circle), - ], - ); - }, - ), - ExampleItem( - desc: '自定义品牌导航栏', - builder: (_) { - return TDNavBar( - title: '品牌名称', - useDefaultBack: false, - useBorderStyle: true, - centerTitle: false, - titleFontWeight: FontWeight.w600, - rightBarItems: [ - TDNavBarItem(icon: TDIcons.ellipsis), - TDNavBarItem(icon: TDIcons.add_circle), - ], - ); - }, - ), - ExampleItem( - desc: '自定义图片导航栏', - builder: (_) { - return TDNavBar( - titleWidget: Container( - width: 200, - color: Colors.red, - height: 20, - ), - useDefaultBack: false, - useBorderStyle: true, - centerTitle: false, - rightBarItems: [ - TDNavBarItem(icon: TDIcons.ellipsis), - TDNavBarItem(icon: TDIcons.add_circle), - ], - ); - }, - ), - ExampleItem( - desc: '品牌超长文字导航栏', - builder: (_) { - return TDNavBar( - title: '品牌名称最长最长最长最长最长最长最长最长最长最长最长', - useDefaultBack: false, - useBorderStyle: true, - centerTitle: false, - titleFontWeight: FontWeight.w600, - rightBarItems: [ - TDNavBarItem(icon: TDIcons.ellipsis), - TDNavBarItem(icon: TDIcons.add_circle), - ], - ); - }, - ), - ExampleItem( - desc: '自定义背景色', - builder: (_) { - return TDNavBar( - title: '标题', - titleColor: Colors.white, - backgroundColor: TDTheme.of(context).brandColor8, - useDefaultBack: false, - ); - }, - ), - ], - ); - } -} diff --git a/example/lib/tdesign/page/td_picker_page.dart b/example/lib/tdesign/page/td_picker_page.dart deleted file mode 100644 index 3f1fbcc93..000000000 --- a/example/lib/tdesign/page/td_picker_page.dart +++ /dev/null @@ -1,237 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - - -class TDPickerPage extends StatefulWidget { - const TDPickerPage({Key? key}) : super(key: key); - - @override - State createState() => _TDPickerPageState(); -} - -class _TDPickerPageState extends State { - String selected_1 = ''; - List data_1 = ['广州市', '韶关市', '深圳市', '珠海区', '汕头市']; - String selected_2 = ''; - String selected_3 = ''; - List> data_3 = []; - String selected_4 = ''; - Map data_4 = { - '广东省': { - '深圳市': ['南山区', '宝安区', '罗湖区', '福田区'], - '佛山市': [''], - '广州市': ['花都区'] - }, - '重庆市': { - '重庆市': ['九龙坡区', '江北区'] - }, - '浙江省': { - '杭州市': ['西湖区', '余杭区', '萧山区'], - '宁波市': ['江东区', '北仑区', '奉化市'] - }, - '香港': { - '香港': ['九龙城区', '黄大仙区', '离岛区', '湾仔区'] - } - }; - - String selected_5 = ''; - String selected_6 = ''; - String selected_7 = ''; - String selected_8 = ''; - - @override - void initState() { - var list = []; - for(var i = 2022; i >= 2000; i--) { - list.add('${i}年'); - } - data_3.add(list); - data_3.add(['春', '夏', '秋', '冬']); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '选择器 Picker', - children: [ - ExampleItem( - desc: '基础选择器--城市', - builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '选择城市', - onTap: () => TDPicker.showMultiPicker(context, title: '', - onConfirm: (selected) { - setState(() { - selected_1 = '城市: ${data_1[selected[0]]}'; - }); - }, data: [data_1], pickerHeight: 168, pickerItemCount: 4), - ), - TDText( - selected_1, - ), - ]); - }), - ExampleItem(desc: '基础选择器--年份和季节', builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '选择年份和季节', - onTap: () => TDPicker.showMultiPicker(context, title: '', - onConfirm: (selected) { - setState(() { - selected_3 = - '年份和季节: ${data_3[0][selected[0]]} ${data_3[1][selected[1]]}'; - }); - }, data: data_3, initialIndexes: [1, 2]), - ), - TDText( - selected_3, - ), - ]); - }), - ExampleItem(desc: '基础选择器--日期', builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '选择日期', - onTap: () => TDPicker.showDatePicker(context, title: '', - onConfirm: (selected) { - setState(() { - selected_2 = '日期: $selected'; - }); - }, - dateStart: [2010, 12, 20], - dateEnd: [2022, 2, 28], - initialDate: [2012, 1, 1]), - ), - TDText( - selected_2, - ), - ]); - }), - ExampleItem(desc: '基础选择器--地区--联动', builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '选择地区', - onTap: () => TDPicker.showMultiLinkedPicker(context, title: '', - onConfirm: (selected) { - setState(() { - selected_4 = '组合: ${selected[0]} ${selected[1]} ${selected[2]}'; - }); - }, - pickerHeight: 168, - data: data_4, - pickerItemCount: 4, - columnNum: 3, - initialData: ['浙江省', '杭州市', '西湖区']), - ), - TDText( - selected_4, - ), - ]); - }), - ExampleItem( - desc: '带标题--城市', - builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '带标题-选择城市', - onTap: () => TDPicker.showMultiPicker(context, title: '选择城市', - onConfirm: (selected) { - setState(() { - selected_5 = '城市: ${data_1[selected[0]]}'; - }); - }, data: [data_1], pickerHeight: 168, pickerItemCount: 4), - ), - TDText( - selected_5, - ), - ]); - }), - ExampleItem(desc: '带标题--年份和四季', builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '带标题-年份和季节', - onTap: () => TDPicker.showMultiPicker(context, title: '选择年份和季节', - onConfirm: (selected) { - setState(() { - selected_6 = - '年份和季节: ${data_3[0][selected[0]]} ${data_3[1][selected[1]]}'; - }); - }, data: data_3, initialIndexes: [1, 2]), - ), - TDText( - selected_6, - ), - ]); - }), - ExampleItem(desc: '带标题--日期', builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '带标题-选择日期', - onTap: () => TDPicker.showDatePicker(context, title: '选择日期', - onConfirm: (selected) { - setState(() { - selected_7 = '日期: $selected'; - }); - }, - dateStart: [2010, 12, 20], - dateEnd: [2022, 2, 28], - initialDate: [2012, 1, 1]), - ), - TDText( - selected_7, - ), - ]); - }), - ExampleItem(desc: '基础选择器--地区--联动', builder: (_) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDButton( - width: 200, - content: '带标题-选择地区', - onTap: () => TDPicker.showMultiLinkedPicker(context, title: '选择地区', - onConfirm: (selected) { - setState(() { - selected_8 = '组合: ${selected[0]} ${selected[1]} ${selected[2]}'; - }); - }, - pickerHeight: 168, - data: data_4, - pickerItemCount: 4, - columnNum: 3, - initialData: ['浙江省', '杭州市', '西湖区']), - ), - TDText( - selected_8, - ), - ]); - }), - ], - ); - } -} diff --git a/example/lib/tdesign/page/td_popup_page.dart b/example/lib/tdesign/page/td_popup_page.dart deleted file mode 100644 index 4fa4946b1..000000000 --- a/example/lib/tdesign/page/td_popup_page.dart +++ /dev/null @@ -1,105 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; -import 'package:tdesign_flutter_example/tdesign/example_base.dart'; - -/// -/// TDPopup掩饰 -/// -class TDPopupPage extends StatefulWidget { - const TDPopupPage({Key? key}) : super(key: key); - - @override - State createState() { - return TDPopupPageState(); - } -} - -class TDPopupPageState extends State { - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - Widget current = Column( - children: [ - TDButton( - content: '顶部Popup', - onTap: () { - Navigator.of(context).push(TDSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.top, - builder: (context) { - return Container( - color: Colors.white, - height: 300, - ); - })); - }, - ), - - const SizedBox(height: 10,), - - TDButton( - content: '底部Popup', - onTap: () { - Navigator.of(context).push(TDSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.bottom, - builder: (context) { - return Container( - color: Colors.white, - height: 300, - ); - })); - }, - ), - - const SizedBox(height: 10,), - - TDButton( - content: '左侧Popup', - onTap: () { - Navigator.of(context).push(TDSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.left, - builder: (context) { - return Container( - color: Colors.white, - width: 300, - ); - })); - }, - ), - - - const SizedBox(height: 10,), - - TDButton( - content: '右侧Popup', - onTap: () { - Navigator.of(context).push(TDSlidePopupRoute( - slideTransitionFrom: SlideTransitionFrom.right, - builder: (context) { - return Container( - color: Colors.white, - width: 300, - ); - })); - }, - ), - ], - ); - - current = Padding( - padding: const EdgeInsets.all(20), - child: current, - ); - - current = ExampleWidget( - title: '弹出层 PopUp', - children: [ - current, - ], - ); - return current; - } -} diff --git a/example/lib/tdesign/page/td_radio_page.dart b/example/lib/tdesign/page/td_radio_page.dart deleted file mode 100644 index ba9241004..000000000 --- a/example/lib/tdesign/page/td_radio_page.dart +++ /dev/null @@ -1,250 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - -/// -/// TDRadio演示 -/// -class TDRadioPage extends StatefulWidget { - const TDRadioPage({Key? key}) : super(key: key); - - @override - State createState() { - return TDRadioPageState(); - } -} - -class TDRadioPageState extends State { - - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '单选框 Radio', - children: [ - ExampleItem(desc: '基础文本框', builder: (context){ - return TDRadioGroup( - selectId: 'index:0', - child: ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - var title = '单选'; - var subTitle = ''; - if(index == 2){ - title = '单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选'; - } - if(index == 3){ - subTitle = '单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选'; - } - return TDRadio( - id: 'index:$index', - title: title, - titleMaxLine: 2, - subTitleMaxLine: 2, - subTitle: subTitle, - ); - }, - itemCount: 4, - ), - ); - }), - ExampleItem(desc: '左边勾选样式', builder: (context){ - return TDRadioGroup( - radioStyle: TDRadioStyle.check, - selectId: 'index:1', - child: ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - var title = '单选'; - var subTitle = ''; - if(index == 2){ - title = '单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选'; - } - if(index == 3){ - subTitle = '单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选单选'; - } - return TDRadio( - id: 'index:$index', - title: title, - titleMaxLine: 2, - subTitleMaxLine: 2, - subTitle: subTitle, - ); - }, - itemCount: 4, - ), - ); - }), - ExampleItem(desc: '右侧勾选样式', builder: (context) { - return TDRadioGroup( - radioStyle: TDRadioStyle.check, - contentDirection: TDContentDirection.left, - selectId: 'index:0', - child: ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - return TDRadio( - id: 'index:$index', - title: '单选$index', - ); - }, - itemCount: 4, - ), - ); - }), - ExampleItem(desc: '右侧圆形单选框', builder: (context){ - return TDRadioGroup( - contentDirection: TDContentDirection.left, - selectId: 'index:0', - child: ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (BuildContext context, int index) { - return TDRadio( - id: 'index:$index', - title: '单选$index', - ); - }, - itemCount: 4, - ), - ); - }), - ExampleItem(desc: '自定义图标单选框', builder: (context){ - return TDRadioGroup( - contentDirection: TDContentDirection.right, - child: Column( - children: const [ - TDRadio( - id: '0', - title: '单选', - radioStyle: TDRadioStyle.square, - ), - TDRadio( - id: '1', - title: '单选', - radioStyle: TDRadioStyle.square, - ), - ], - ), - ); - }), - ExampleItem(desc: '规格', builder: (context){ - return TDRadioGroup( - contentDirection: TDContentDirection.right, - child: Column( - children: const [ - TDRadio( - id: '0', - title: '单选 H48', - radioStyle: TDRadioStyle.circle, - ), - TDRadio( - id: '1', - size: TDCheckBoxSize.large, - title: '单选 H56', - radioStyle: TDRadioStyle.circle, - ), - ], - ), - ); - }), - ExampleItem(desc: '状态', builder: (context){ - return TDRadioGroup( - contentDirection: TDContentDirection.right, - selectId: '0', - child: Column( - children: const [ - TDRadio( - id: '0', - title: '单选', - radioStyle: TDRadioStyle.circle, - enable: false, - ), - TDRadio( - id: '1', - title: '单选', - radioStyle: TDRadioStyle.circle, - enable: false, - ), - ], - ), - ); - }), - ExampleItem(desc: '状态', builder: (context){ - return TDRadioGroup( - contentDirection: TDContentDirection.right, - selectId: '0', - child: Column( - children: const [ - TDRadio( - id: '0', - title: '单选', - radioStyle: TDRadioStyle.check, - enable: false, - ), - TDRadio( - id: '1', - title: '单选', - radioStyle: TDRadioStyle.check, - enable: false, - ), - ], - ), - ); - }), - ExampleItem(desc: '状态', builder: (context){ - return TDRadioGroup( - contentDirection: TDContentDirection.left, - selectId: '0', - child: Column( - children: const [ - TDRadio( - id: '0', - title: '单选', - radioStyle: TDRadioStyle.circle, - enable: false, - ), - TDRadio( - id: '1', - title: '单选', - radioStyle: TDRadioStyle.circle, - enable: false, - ), - ], - ), - ); - }), - ExampleItem(desc: '状态', builder: (context){ - return TDRadioGroup( - contentDirection: TDContentDirection.left, - selectId: '0', - child: Column( - children: const [ - TDRadio( - id: '0', - title: '单选', - radioStyle: TDRadioStyle.check, - enable: false, - ), - TDRadio( - id: '1', - title: '单选', - radioStyle: TDRadioStyle.check, - enable: false, - ), - ], - ), - ); - }), - ] - ); - } -} diff --git a/example/lib/tdesign/page/td_refresh_page.dart b/example/lib/tdesign/page/td_refresh_page.dart deleted file mode 100644 index 047762736..000000000 --- a/example/lib/tdesign/page/td_refresh_page.dart +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Created by haozhicao@tencent.com on 6/28/22. - * td_loading_page.dart - * - */ - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_easyrefresh/easy_refresh.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - -class TdPullDownRefreshPage extends StatefulWidget { - const TdPullDownRefreshPage({Key? key}) : super(key: key); - - @override - State createState() => _TdPullDownRefreshPageState(); -} - -class _TdPullDownRefreshPageState extends State { - var itemCount = 10; - - var dataList = List.generate(10, (index) => '首页$index'); - - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '下拉刷新 PullDownRefresh', - children: [ - SizedBox( - height: 1000, - child: Stack( - children: [ - EasyRefresh( - // 下拉样式 - header: TDRefreshHeader(), - child: ListView.builder( - itemBuilder: (context, index) => Text('${dataList[index]}'), - itemCount: dataList.length, - ), - // 下拉刷新回调 - onRefresh: () async { - await Future.delayed(const Duration(seconds: 2), () { - dataList.addAll( - List.generate(10, (index) => ' 下拉添加的第$index个item')); - setState(() {}); - }); - }, - ) - ], - ), - ) - ]); - } -} diff --git a/example/lib/tdesign/page/td_search_bar_page.dart b/example/lib/tdesign/page/td_search_bar_page.dart deleted file mode 100644 index 8368760ca..000000000 --- a/example/lib/tdesign/page/td_search_bar_page.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:tdesign_flutter/td_export.dart'; -import '../example_base.dart'; - -class TDSearchBarPage extends StatefulWidget { - const TDSearchBarPage({Key? key}) : super(key: key); - - @override - State createState() => _TDSearchBarPageState(); -} - -class _TDSearchBarPageState extends State { - String? inputText; - - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '搜索框 Search', - children: [ - TDSearchBar( - placeHolder: '搜索预设文案', - onTextChanged: (String text) { - setState(() { - inputText = text; - }); - }, - ), - Row( - children: [ - TDText( - '输入文案:', - font: TDTheme.of(context).fontM, - textColor: TDTheme.of(context).fontGyColor1, - ), - TDText( - '${inputText ?? ''}', - font: TDTheme.of(context).fontM, - textColor: TDTheme.of(context).fontGyColor1, - ), - ], - ) - ], - backgroundColor: TDTheme.of(context).whiteColor1, - ); - } -} diff --git a/example/lib/tdesign/page/td_swiper_page.dart b/example/lib/tdesign/page/td_swiper_page.dart deleted file mode 100644 index 53dd78cf5..000000000 --- a/example/lib/tdesign/page/td_swiper_page.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - -class TDSwiperPage extends StatelessWidget { - const TDSwiperPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '轮播图 Swiper', - children: [ - ExampleItem( - desc: '圆点指示器', - builder: (_) { - return Container( - height: 100, - decoration: BoxDecoration( - color: Colors.blueAccent.withOpacity(0.8), - borderRadius: BorderRadius.circular(10)), - child: Swiper( - autoplay: true, - itemCount: 6, - loop: true, - pagination: const SwiperPagination( - alignment: Alignment.bottomCenter, - builder: TDSwiperPagination.dots), - itemBuilder: (BuildContext context, int index) { - return FlutterLogo( - size: MediaQuery.of(context).size.width, - ); - }, - ), - ); - }), - ExampleItem( - desc: '圆角矩形指示器(默认100ms动画)', - builder: (_) { - return Container( - height: 100, - decoration: BoxDecoration( - color: Colors.blueAccent.withOpacity(0.8), - borderRadius: BorderRadius.circular(10)), - child: Swiper( - autoplay: true, - itemCount: 6, - loop: true, - pagination: const SwiperPagination( - alignment: Alignment.bottomCenter, - builder: TDSwiperPagination.roundedRectangle), - itemBuilder: (BuildContext context, int index) { - return FlutterLogo( - size: MediaQuery.of(context).size.width, - ); - }, - ), - ); - }), - ExampleItem( - desc: '数字指示器', - builder: (_) { - return Container( - height: 100, - decoration: BoxDecoration( - color: Colors.blueAccent.withOpacity(0.8), - borderRadius: BorderRadius.circular(10)), - child: Swiper( - autoplay: true, - itemCount: 6, - loop: true, - pagination: const SwiperPagination( - alignment: Alignment.bottomCenter, - builder: TDSwiperPagination.fraction), - itemBuilder: (BuildContext context, int index) { - return FlutterLogo( - size: MediaQuery.of(context).size.width, - ); - }, - ), - ); - }), - ExampleItem( - desc: '数字指示器指定位置', - builder: (_) { - return Container( - height: 100, - decoration: BoxDecoration( - color: Colors.blueAccent.withOpacity(0.8), - borderRadius: BorderRadius.circular(10)), - child: Swiper( - autoplay: true, - itemCount: 6, - loop: true, - pagination: const SwiperPagination( - alignment: Alignment.topRight, - builder: TDSwiperPagination.fraction), - itemBuilder: (BuildContext context, int index) { - return FlutterLogo( - size: MediaQuery.of(context).size.width, - ); - }, - ), - ); - }), - ExampleItem( - desc: '箭头指示器', - builder: (_) { - return Container( - height: 100, - decoration: BoxDecoration( - color: Colors.blueAccent.withOpacity(0.8), - borderRadius: BorderRadius.circular(10)), - child: Swiper( - autoplay: true, - itemCount: 6, - loop: true, - pagination: const SwiperPagination( - alignment: Alignment.center, - builder: TDSwiperPagination.arrow), - itemBuilder: (BuildContext context, int index) { - return FlutterLogo( - size: MediaQuery.of(context).size.width, - ); - }, - ), - ); - }), - ExampleItem( - desc: '箭头指示器非循环,边界箭头隐藏', - builder: (_) { - return Container( - height: 100, - decoration: BoxDecoration( - color: Colors.blueAccent.withOpacity(0.8), - borderRadius: BorderRadius.circular(10)), - child: Swiper( - autoplay: true, - itemCount: 6, - loop: false, - pagination: const SwiperPagination( - alignment: Alignment.center, - builder: TDSwiperPagination.arrow), - itemBuilder: (BuildContext context, int index) { - return FlutterLogo( - size: MediaQuery.of(context).size.width, - ); - }, - ), - ); - }), - ], - ); - } -} diff --git a/example/lib/tdesign/page/td_switch_page.dart b/example/lib/tdesign/page/td_switch_page.dart deleted file mode 100644 index 0ae89cdd3..000000000 --- a/example/lib/tdesign/page/td_switch_page.dart +++ /dev/null @@ -1,148 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; -import 'package:tdesign_flutter_example/tdesign/example_base.dart'; - -/// -/// TdSwitchPage演示 -/// -class TDSwitchPage extends StatefulWidget { - const TDSwitchPage({Key? key}) : super(key: key); - - @override - State createState() { - return TDSwitchPageState(); - } -} - -class TDSwitchPageState extends State { - @override - Widget build(BuildContext context) { - Widget current = ListView( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - children: [ - _title('基础开关'), - Container( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - demoRow(context, '开关', on: true), - _divider(), - demoRow(context, '开关', on: false), - _divider(), - demoRow(context, '自定义颜色', on: true, onColor: Colors.green), - _divider(), - demoRow(context, '自定义颜色', - on: false, onColor: Colors.green, offColor: Colors.brown), - _divider(), - demoRow(context, '异步操作', on: true), - ], - ), - padding: const EdgeInsets.only(left: 16, right: 16), - color: Colors.white, - ), - _title('带描述状态开关'), - Container( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - demoRow(context, '开关', desc: '描述信息', on: true), - _divider(), - demoRow(context, '开关', desc: '描述信息', on: false), - _divider(), - demoRow(context, '自定义颜色', - desc: '描述信息', on: true, onColor: Colors.green), - ], - ), - padding: const EdgeInsets.only(left: 16, right: 16), - color: Colors.white, - ), - _title('状态'), - Container( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - demoRow(context, '开关开启禁用', on: true, enable: false), - _divider(), - demoRow(context, '开关开启禁用', on: false, enable: false), - _divider(), - demoRow(context, '开关开启禁用', - on: true, onColor: Colors.green, enable: false), - _divider(), - demoRow(context, '开关开启禁用', - desc: '描述信息', on: false, enable: false), - _divider(), - demoRow(context, '开关开启禁用', desc: '描述信息', on: true, enable: false), - ], - ), - padding: const EdgeInsets.only(left: 16, right: 16), - color: Colors.white, - ), - ], - ); - - current = Container( - child: current, - color: TDTheme.of(context).grayColor1, - ); - - - current = ExampleWidget( - title: '开关 Switch', - children: [ - current - ], - ); - return current; - } - - Widget _divider() { - return Container(height: 0.5, color: const Color(0x33999999)); - } - - Widget _title(String title) { - return Container( - height: 40, - padding: const EdgeInsets.only(left: 10), - alignment: Alignment.centerLeft, - child: Text(title), - ); - } - - Widget demoRow( - BuildContext context, - String? title, { - String? desc, - bool on = true, - bool enable = true, - Color? onColor, - Color? offColor, - }) { - final theme = TDTheme.of(context); - Widget current = Row( - children: [ - Expanded( - child: TDText( - title, - textColor: theme.fontGyColor1, - )), - TDText( - desc ?? '', - textColor: theme.grayColor6, - ), - TDSwitch( - isOn: on, - onColor: onColor, - enable: enable, - offColor: offColor, - ) - ], - ); - current = SizedBox( - child: current, - height: 44, - ); - return current; - } -} diff --git a/example/lib/tdesign/page/td_tabbar_page.dart b/example/lib/tdesign/page/td_tabbar_page.dart deleted file mode 100644 index 4b7e8310c..000000000 --- a/example/lib/tdesign/page/td_tabbar_page.dart +++ /dev/null @@ -1,157 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; -import '../example_base.dart'; - - -class TDTabBarPage extends StatefulWidget { - const TDTabBarPage({Key? key}) : super(key: key); - - @override - State createState() => _TDTabBarPageState(); -} - -class _TDTabBarPageState extends State - with TickerProviderStateMixin { - TabController? _tabController; - TabController? _tabController1; - TabController? _tabController2; - TabController? _tabController3; - TabController? _tabController4; - TabController? _tabController5; - List tabs = []; - List tabViews = []; - - List _getTabs() { - tabs = const [ - TDTab(text: '标签页一'), - TDTab(text: '标签页二'), - TDTab(text: '标签页三'), - TDTab(text: '标签页四'), - TDTab(text: '标签页五'), - ]; - return tabs; - } - - List _getTabViews() { - tabViews = const [ - Center(child: TDText('内容一')), - Center(child: TDText('内容二')), - Center(child: TDText('内容三')), - Center(child: TDText('内容四')), - ]; - return tabViews; - } - - @override - void initState() { - _initTabController(); - _getTabs(); - super.initState(); - } - - List subList(int length) { - var temp = []; - for(var i = 0; i < length; i++) { - temp.add(tabs[i]); - } - return temp; - } - - //初始化tab - void _initTabController() { - _tabController1 = TabController(length: 2, vsync: this); - _tabController2 = TabController(length: 3, vsync: this); - _tabController3 = TabController(length: 4, vsync: this); - _tabController4 = TabController(length: 5, vsync: this); - _tabController5 = TabController(length: 4, vsync: this); - _tabController = TabController(length: 5, vsync: this); - } - - @override - Widget build(BuildContext context) { - return ExampleWidget( - title: '选项卡 Tabs TDTabBar', - padding: const EdgeInsets.symmetric(vertical: 16), - children: [ - ExampleItem( - desc: '横向选项卡', - builder: (BuildContext context) { - return Column( - children: [ - TDTabBar( - tabs: subList(2), - controller: _tabController1, - backgroundColor: Colors.white, - showIndicator: true, - ), - TDTabBar( - tabs: subList(3), - controller: _tabController2, - backgroundColor: Colors.white, - showIndicator: true, - ), - TDTabBar( - tabs: subList(4), - controller: _tabController3, - backgroundColor: Colors.white, - showIndicator: true, - ), - TDTabBar( - tabs: _getTabs(), - controller: _tabController4, - backgroundColor: Colors.white, - showIndicator: true, - isScrollable: true, - ), - ], - ); - }, - ), - ExampleItem( - desc: '无下划线横向选项卡', - builder: (BuildContext context) { - return TDTabBar( - tabs: _getTabs(), - controller: _tabController, - backgroundColor: Colors.white, - isScrollable: true, - ); - }, - ), - ExampleItem( - desc: '竖向选项卡', - builder: (BuildContext context) { - return LayoutBuilder(builder: (context, constraints){ - - return SizedBox( - width: constraints.maxWidth, - height: 4 * 54, - child: Row( - children: [ - TDTabBar( - width: 104, - tabs: subList(4), - controller: _tabController5, - showIndicator: true, - backgroundColor: Colors.white, - isScrollable: false, - isVertical: true - ), - Container( - width: constraints.maxWidth - 104, - color: Colors.white, - child: TDTabBarVerticalView( - children: _getTabViews(), - controller: _tabController5, - ), - ) - ], - ), - ); - }); - }, - ), - ], - ); - } -} diff --git a/example/lib/tdesign/page/td_tag_page.dart b/example/lib/tdesign/page/td_tag_page.dart deleted file mode 100644 index 9b591e1da..000000000 --- a/example/lib/tdesign/page/td_tag_page.dart +++ /dev/null @@ -1,229 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; -import '../example_base.dart'; - -class TDTagPage extends StatelessWidget { - const TDTagPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ExampleWidget(title: '标签 Tag', - // padding: EdgeInsets.zero, - children: [ - ExampleItem( - desc: '展示标签', - builder: (context) { - return Wrap( - spacing: 16, - runSpacing: 16, - children: [ - const TDTag( - '标签', - ), - const TDTag('成功', - style: RoundRectTagStyle(type: TDTagType.success)), - const TDTag('警告', - style: RoundRectTagStyle(type: TDTagType.warning)), - const TDTag('危险', - style: RoundRectTagStyle(type: TDTagType.error)), - const TDTag('信息', - style: RoundRectTagStyle(type: TDTagType.message)), - const TDTag('浅色', style: RoundRectTagStyle(isLight: true)), - const TDTag('描边', style: WireframeRoundRectTagStyle()), - const TDTag('浅色描边', - style: WireframeRoundRectTagStyle(isLight: true)), - const TDTag( - 'English', - ), - const TDTag('English', - style: WireframeRoundRectTagStyle(isLight: true)), - const TDTag( - 'ABC', - ), - const TDTag('ABC', - style: WireframeRoundRectTagStyle(isLight: true)), - const TDTag( - '中English混合', - ), - const TDTag('中English混合', - style: WireframeRoundRectTagStyle(isLight: true)), - TDTag( - '圆角', - style: CircleRectTagStyle(), - ), - TDTag( - '半圆', - style: SemicircleRectTagStyle(), - ), - TDTag( - '标签', - needCloseIcon: true, - onCloseTap: () { - TDToast.showText('点击了关闭图标', context: context); - }, - ), - ], - ); - }), - ExampleItem( - desc: '点击控件', - builder: (context) { - return Wrap( - spacing: 16, - runSpacing: 16, - children: [ - TDSelectTag( - '标签', - isSelected: true, - onSelectChanged: (isSelect) { - TDToast.showText('标签选中:$isSelect', context: context); - }, - ), - TDSelectTag( - '标签', - onSelectChanged: (isSelect) { - TDToast.showText('标签选中:$isSelect', context: context); - }, - ), - TDSelectTag( - '标签', - style: CircleRectTagStyle(), - unSelectStyle: CircleRectTagStyle( - textColor: TDTheme.of(context).fontGyColor1, - backgroundColor: TDTheme.of(context).grayColor3, - ), - onSelectChanged: (isSelect) { - TDToast.showText('标签选中:$isSelect', context: context); - }, - ), - TDSelectTag( - '标签', - style: SemicircleRectTagStyle(), - unSelectStyle: SemicircleRectTagStyle( - textColor: TDTheme.of(context).fontGyColor1, - backgroundColor: TDTheme.of(context).grayColor3, - ), - onSelectChanged: (isSelect) { - TDToast.showText('标签选中:$isSelect', context: context); - }, - ), - TDSelectTag( - '标签', - onSelectChanged: (isSelect) { - TDToast.showText('标签选中:$isSelect', context: context); - }, - enableSelect: false, - ), - TDSelectTag( - '标签', - style: CircleRectTagStyle(), - unSelectStyle: CircleRectTagStyle( - textColor: TDTheme.of(context).fontGyColor1, - backgroundColor: TDTheme.of(context).grayColor3, - ), - unEnableSelectStyle: CircleRectTagStyle( - textColor: TDTheme.of(context).fontGyColor4, - backgroundColor: TDTheme.of(context).grayColor3, - ), - enableSelect: false, - ), - - TDSelectTag( - '标签', - onSelectChanged: (isSelect) { - TDToast.showText('标签选中:$isSelect', context: context); - }, - needCloseIcon: true, - onCloseTap: (){ - TDToast.showText('点击关闭标签', context: context); - }, - ), - TDSelectTag( - '标签', - style: CircleRectTagStyle(), - unSelectStyle: CircleRectTagStyle( - textColor: TDTheme.of(context).fontGyColor1, - backgroundColor: TDTheme.of(context).grayColor3, - ), - unEnableSelectStyle: CircleRectTagStyle( - textColor: TDTheme.of(context).fontGyColor4, - backgroundColor: TDTheme.of(context).grayColor3, - ), - needCloseIcon: true, - onCloseTap: (){ - TDToast.showText('点击关闭标签', context: context); - }, - ), - ], - ); - }), - ExampleItem( - desc: '尺寸规格-大,正常,小', - builder: (context) { - return Wrap( - spacing: 16, - runSpacing: 16, - children: const [ - TDTag( - '标签', - size: TDTagSize.large, - ), - TDTag('标签', size: TDTagSize.middle), - TDTag( - '标签', - size: TDTagSize.small, - ), - ], - ); - }), - ExampleItem( - desc: '圆角尺寸规格-大,正常,小', - builder: (context) { - return Wrap( - spacing: 16, - runSpacing: 16, - children: [ - TDTag( - '圆角', - style: CircleRectTagStyle(), - size: TDTagSize.large, - ), - TDTag( - '圆角', - style: CircleRectTagStyle(), - ), - TDTag( - '圆角', - style: CircleRectTagStyle(), - size: TDTagSize.small, - ), - ], - ); - }), - ExampleItem( - desc: '半圆尺寸规格-大,正常,小', - builder: (context) { - return Wrap( - spacing: 16, - runSpacing: 16, - children: [ - TDTag( - '半圆', - style: SemicircleRectTagStyle(), - size: TDTagSize.large, - ), - TDTag( - '半圆', - style: SemicircleRectTagStyle(), - ), - TDTag( - '半圆', - style: SemicircleRectTagStyle(), - size: TDTagSize.small, - ), - ], - ); - }), - ]); - } -} diff --git a/example/lib/tdesign/page/td_text_page.dart b/example/lib/tdesign/page/td_text_page.dart deleted file mode 100644 index b0d1fd80a..000000000 --- a/example/lib/tdesign/page/td_text_page.dart +++ /dev/null @@ -1,154 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../example_base.dart'; - -class TDTextPage extends StatelessWidget { - const TDTextPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - var exampleTxt = '文本Text'; - // debugPaintBaselinesEnabled = true; - return ExampleWidget( - padding: const EdgeInsets.all(8), - title: 'TDText', - children: [ - ExampleItem( - desc: '系统Text:', - builder: (_) { - return Text( - exampleTxt, - ); - }), - ExampleItem( - desc: '普通TDText:', - builder: (_) { - return TDText( - exampleTxt, - ); - }), - ExampleItem( - desc: '指定常用属性:', - builder: (_) { - return TDText( - exampleTxt, - font: TDTheme.of(context).fontXL, - textColor: TDTheme.of(context).brandNormalColor, - backgroundColor: TDTheme.of(context).successHoverColor, - ); - }), - ExampleItem( - desc: 'style覆盖textColor,不覆盖font:', - builder: (_) { - return TDText( - exampleTxt, - font: TDTheme.of(context).fontM, - textColor: TDTheme.of(context).brandNormalColor, - style: - TextStyle(color: TDTheme.of(context).errorNormalColor), - ); - }), - ExampleItem( - desc: 'style覆盖textColor和font:', - builder: (_) { - return TDText( - exampleTxt, - font: TDTheme.of(context).fontM, - textColor: TDTheme.of(context).brandNormalColor, - ); - }), - ExampleItem( - desc: 'TDText.rich测试:', - builder: (_) { - return TDText.rich( - TextSpan(children: [ - TDTextSpan( - text: 'TDTextSpan1', - font: TDTheme.of(context).fontL, - textColor: TDTheme.of(context).warningNormalColor, - isTextThrough: true, - lineThroughColor: TDTheme.of(context).brandNormalColor, - style: TextStyle( - color: TDTheme.of(context).errorNormalColor)), - TextSpan( - text: 'TextSpan2', - style: TextStyle( - fontSize: 14, - color: TDTheme.of(context).brandNormalColor)), - const WidgetSpan( - child: Icon(TDIcons.setting, size: 24,) - ), - ]), - font: TDTheme.of(context).fontM, - textColor: TDTheme.of(context).brandNormalColor, - style: TextStyle( - color: TDTheme.of(context).errorNormalColor, fontSize: 32), - ); - }), - ExampleItem( - desc: '获取系统Text:', - builder: (_) { - return TDText( - exampleTxt, - backgroundColor: TDTheme.of(context).successHoverColor, - ).getRawText(context: context); - }), - ExampleItem( - desc: '中文居中:(带有英文可能不居中)', - builder: (_) { - return const TDText( - '中华人民共和国腾讯科技', - // font: Font(size: 100, lineHeight: 100), - forceVerticalCenter: true, - backgroundColor: Colors.orange, - ); - }), - ExampleItem( - desc: '自定义内部padding:', - builder: (_) { - return TDTextConfiguration( - paddingConfig: CustomTextPaddingConfig(), - child: const CustomPaddingText(), - ); - }), - ], - ); - } -} - -/// 自定义控件,内部的context可拿到外部TDTextConfiguration的配置信息 -class CustomPaddingText extends StatelessWidget { - const CustomPaddingText({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - const TDText( - '中华人民共和国腾讯科技fgjpqy', - // font: Font(size: 100, lineHeight: 100), - forceVerticalCenter: true, - backgroundColor: Colors.orange, - ), - TDText( - 'English', - font: TDTheme.of(context).fontXL, - forceVerticalCenter: true, - backgroundColor: Colors.orange, - ), - ], - ); - } -} - -/// 重写内部padding方法 -class CustomTextPaddingConfig extends TDTextPaddingConfig { - @override - EdgeInsetsGeometry getPadding(String data, double fontSize, double height) { - var supperPadding = super.getPadding(data, fontSize, height); - return EdgeInsets.only(left: 30, top: supperPadding.vertical.toDouble()); - } -} - diff --git a/example/lib/tdesign/page/td_theme_page.dart b/example/lib/tdesign/page/td_theme_page.dart deleted file mode 100644 index 532865de9..000000000 --- a/example/lib/tdesign/page/td_theme_page.dart +++ /dev/null @@ -1,122 +0,0 @@ -import 'package:flutter/material.dart'; - -/// 组件库相关的,只需要引入这个文件,里面暴露td前缀所有需要的类 -import 'package:tdesign_flutter/td_export.dart'; -import 'package:tdesign_flutter_example/tdesign/example_base.dart'; - -/// 主题示例页 - -class TDThemePage extends StatefulWidget { - const TDThemePage({Key? key}) : super(key: key); - - @override - _TDThemePageState createState() => _TDThemePageState(); -} - -class _TDThemePageState extends State { - @override - Widget build(BuildContext context) { - return ExampleWidget(title: '主题示例', children: [ - Container( - color: Colors.white, - alignment: Alignment.center, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - /// 使用示例 - TDText( - '使用外层待赋值主题', - font: - TDTheme.of(context).fontM, // 业务方使用时,如果明确跟随全局主题,可以不传context - textColor: - TDTheme.of(context).brandNormalColor, // 点击颜色可查看具体设置和显示效果 - ), - - /// 使用示例 - TDText( - '使用外层不赋值主题', - font: - TDTheme.of(context).fontL, // 业务方使用时,如果明确跟随全局主题,可以不传context - textColor: - TDTheme.of(context).successNormalColor, // 点击颜色可查看具体设置和显示效果 - ), - - /// 此处替换主题 - TDTheme( - // 替换fonts和colors,其他主题从父类拷贝 - data: TDTheme.of(context).copyWith('custom', fontMap: { - 'fontM': Font(size: 40, lineHeight: 80), - }, colorMap: { - 'brandNormalColor': Colors.red - }), - // 不能直接在此处使用contxt,这里虽然被包裹在TGTheme中,但是context未更新,因此阿不到最新数据 - child: const TestWidget()), - ], - )) - ]); - } -} - -/// 测试控件 -class TestWidget extends StatelessWidget { - const TestWidget({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TDText( - '使用内层赋值主题', - font: TDTheme.of(context).fontM, //明确使用内层主题,必须传context - textColor: - TDTheme.of(context).brandNormalColor, // 明确使用内层主题,必须传context - ), - TDText( - '使用内层不赋值主题', - font: TDTheme.of(context).fontL, //明确使用内层主题,必须传context - textColor: - TDTheme.of(context).successNormalColor, // 明确使用内层主题,必须传context - ), - TDText( - '使用默认主题', - font: TDTheme.defaultData().fontM, //不传context,使用默认主题,此处是外层的主题 - textColor: TDTheme.defaultData().brandNormalColor, - ), - ], - ), - ); - } -} - -/// 扩展主题属性示例 -extension TGLayouts on TDThemeData { - /// 因为扩展中不能声明字段,只能借助TDExtraThemeData - double get layout1 => ofExtra()?.layouts['layout1'] ?? 0; - - Data2? get data2 => ofExtra()?.data2; -} - -class LayoutExtra extends TDExtraThemeData { - Map layouts = {}; - Data2? data2; - - @override - void parse(String name, Map curThemeMap) { - // TODO: implement parse - } -} - -/// 二级扩展测试 -class Data2 {} - -extension Data2Ext on Data2 { - String get test => 'test'; -} - -void test() { - TDTheme.of(null).layout1; - TDTheme.of(null).data2!.test; -} diff --git a/example/lib/tdesign/page/td_toast_page.dart b/example/lib/tdesign/page/td_toast_page.dart deleted file mode 100644 index 15d0a6c55..000000000 --- a/example/lib/tdesign/page/td_toast_page.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:tdesign_flutter/td_export.dart'; -import '../example_base.dart'; - -class TDToastPage extends StatefulWidget { - const TDToastPage({Key? key}) : super(key: key); - - @override - State createState() => _TDToastPageState(); -} - -class _TDToastPageState extends State { - @override - Widget build(BuildContext context) { - return ExampleWidget(title: '轻提示 Toast', children: [ - ElevatedButton( - onPressed: () { - TDToast.showText('我是Toast', context: context); - }, - child: const Text('普通toast')), - ElevatedButton( - onPressed: () { - TDToast.showIconText('带图标横向', - icon: TDIcons.check_circle, context: context); - }, - child: const Text('带图标横向')), - ElevatedButton( - onPressed: () { - TDToast.showIconText('带图标竖向', - icon: TDIcons.check_circle, - direction: IconTextDirection.vertical, - context: context); - }, - child: const Text('带图标竖向')), - ElevatedButton( - onPressed: () { - TDToast.showSuccess('成功文案', - direction: IconTextDirection.vertical, context: context); - }, - child: const Text('成功文案')), - ElevatedButton( - onPressed: () { - TDToast.showWarning('警告文案', - direction: IconTextDirection.horizontal, context: context); - }, - child: const Text('警告文案')), - ElevatedButton( - onPressed: () { - TDToast.showLoading(context: context); - }, - child: const Text('加载中...')), - const ElevatedButton( - onPressed: TDToast.dismissLoading, child: Text('停止loading')), - ElevatedButton( - onPressed: () { - var sb = StringBuffer('我是'); - for (var i = 0; i < 20; i++) { - sb.write('很长$i'); - } - sb.write('toast'); - TDToast.showText(sb.toString(), context: context); - }, - child: const Text('超长toast')), - ElevatedButton( - onPressed: () { - var sb = StringBuffer('我是'); - for (var i = 0; i < 20; i++) { - sb.write('很长$i'); - } - sb.write('toast'); - TDToast.showText(sb.toString(), - context: context, duration: const Duration(seconds: 10)); - }, - child: const Text('10秒再消失toast')), - ]); - } -} diff --git a/example/lib/web/code_widget.dart b/example/lib/web/code_widget.dart deleted file mode 100644 index ebd33a4d8..000000000 --- a/example/lib/web/code_widget.dart +++ /dev/null @@ -1,111 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:http/http.dart' as http; -import 'package:markdown/markdown.dart' as md; -import 'package:tdesign_flutter/td_export.dart'; - -import 'syntax_highlighter.dart'; - -class CodeWidget extends StatefulWidget { - const CodeWidget({Key? key, this.codePath}) : super(key: key); - - final String? codePath; - - @override - State createState() => _CodeWidgetState(); -} - -class _CodeWidgetState extends State { - String? result; - String? lastApiName; - - @override - void initState() { - super.initState(); - getCodeData(); - } - - Future getCodeData() async { - const defaultResult = '加载失败'; - if (widget.codePath == lastApiName && - result != null && - result != defaultResult) { - return result!; - } - try { - var url = - 'https://raw.githubusercontent.com/TDesignOteam/tdesign-flutter/main/example/lib/tdesign/page/td_${widget.codePath}_page.dart'; - - print('getCodeData url:$url'); - var resp = await http.get(Uri.parse(url)); - result = String.fromCharCodes(resp.body.codeUnits); - lastApiName = widget.codePath; - } catch (e) { - print('getCodeData error: $e'); - } - return result ?? defaultResult; - } - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.topLeft, - child: FutureBuilder( - future: getCodeData(), - builder: (context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - return Column( - children: [ - Container( - height: 36, - color: TDTheme.of(context).brandColor1, - child: Row( - children: [ - GestureDetector( - child: const Icon(TDIcons.file_copy), - onTap: () { - if (result != null) { - Clipboard.setData(ClipboardData(text: result!)); - TDToast.showText('复制成功', context: context); - } else { - TDToast.showText('复制失败', context: context); - } - }, - ), - ], - ), - ), - Expanded( - child: Markdown( - padding: EdgeInsets.zero, - selectable: true, - shrinkWrap: true, - syntaxHighlighter: DartSyntaxHighlighter(), - data: ''' -```dart -${snapshot.data} -``` - ''', - extensionSet: md.ExtensionSet( - md.ExtensionSet.gitHubWeb.blockSyntaxes, - [ - md.EmojiSyntax(), - ...md.ExtensionSet.gitHubWeb.inlineSyntaxes - ], - ), - )) - ], - ); - } else { - return Container( - alignment: Alignment.topLeft, - child: const TDText('加载中…'), - ); - } - }, - )); - } -} diff --git a/example/lib/web/web.dart b/example/lib/web/web.dart deleted file mode 100644 index bcddc323b..000000000 --- a/example/lib/web/web.dart +++ /dev/null @@ -1,311 +0,0 @@ -import 'dart:html' if(dart.library.io) 'web_replace.dart' as html; -import 'dart:ui' as ui; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:tdesign_flutter/td_export.dart'; - -import '../main.dart'; -import '../tdesign/api_widget.dart'; -import '../tdesign/example_base.dart'; -import 'code_widget.dart'; - -class WebMainBody extends StatefulWidget { - const WebMainBody({Key? key}) : super(key: key); - - - @override - State createState() => _WebMainBodyState(); -} - -class _WebMainBodyState extends State { - static const menuWidth = 260.0; - - var screenSizeList = [ - const Size(520, 1080), - const Size(480, 720), - ]; - - String version = 'unknown'; - String? apiPath = 'text'; - String? codePath = 'text'; - String? mobilePath; - - - @override - void initState() { - super.initState(); - WebApiController._state = this; - _getVersion(); - } - - Future _getVersion() async { - version = await rootBundle.loadString('assets/version'); - setState(() {}); - } - - @override - void dispose() { - WebApiController._state = null; - super.dispose(); - } - - void setApiPath({required String? apiPath,required String? mobilePath,String? codePath}){ - Future.delayed(const Duration(milliseconds: 500,), (){ - setState(() { - this.apiPath = apiPath; - this.mobilePath = mobilePath; - this.codePath = codePath; - }); - }); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Expanded(child: Container( - color: TDTheme.of().grayColor2, - child: Stack( - children: [ - - // 菜单放到最上方,防止被屏幕外内容遮挡 - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: menuWidth, - color: TDTheme.of(context).brandColor1, - child: _buildMenu(), - ), - Expanded(child: Container( - color: TDTheme.of(context).whiteColor1, - alignment: Alignment.topLeft, - padding: const EdgeInsets.only(left: 32,top: 32), - child: DetailLayout(apiPath: apiPath, codePath: codePath,), - )), - - Container( - margin: const EdgeInsets.only(left:32, top: 32,right: 32), - child: MobileWidget(page: mobilePath ?? 'TDTextPage',), - ) - ], - ), - - ], - ), - )), - - // 通底 - Container( - height: 50, - color: TDTheme.of(context).brandColor2, - alignment: Alignment.center, - child: TDText('版本号:$version', textColor: TDTheme.of(context).fontGyColor1,), - ) - ], - ); - } - - - Widget _buildMenu(){ - return Column( - children: [ - Container( - height: 75, - alignment: Alignment.centerLeft, - padding: const EdgeInsets.only(left: 16), - color: TDTheme.of(context).brandHoverColor, - child: TDText('TDesign 组件', textColor: TDTheme.of(context).whiteColor1,font: TDTheme.of(context).fontXL,), - ) , - Expanded(child: ListView.builder( - itemCount: examplePageList.length, - shrinkWrap: true, - itemBuilder: (context, index) { - var model = examplePageList[index]; - return Container( - padding: const EdgeInsets.only( - left: 16, right: 16, top: 8, bottom: 8), - alignment: Alignment.topLeft, - decoration: BoxDecoration( - border: Border(bottom: BorderSide(color: TDTheme - .of(context) - .grayColor4, width: 0.5))), - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - WebApiController.setApiPath(apiPath: model.apiPath,mobilePath: model.path, codePath: model.codePath ?? model.apiPath, ); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - TDText(model.text), - Icon(TDIcons.chevron_right, color: TDTheme - .of(context) - .brandHoverColor,) - ], - )), - ); - })) - - ], - ); - } - -} - -class WebHome extends StatelessWidget { - const WebHome({Key? key}) : super(key: key); - - static NavigatorState? navigator; - - @override - Widget build(BuildContext context) { - navigator = Navigator.of(context); - return Container( - color: TDTheme.of(context).whiteColor1, - alignment: Alignment.center, - child: const TDText('Hello World'), - ); - } -} - -class DetailLayout extends StatefulWidget { - const DetailLayout({Key? key, required this.apiPath, this.codePath}) : super(key: key); - - final String? apiPath; - final String? codePath; - - @override - State createState() => _DetailLayoutState(); -} - -class _DetailLayoutState extends State with TickerProviderStateMixin { - - - TabController? _tabController; - - @override - void initState() { - super.initState(); - _tabController = TabController(length: 2, vsync: this); - } - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TDTabBar( - tabs: _getTabs(), - controller: _tabController, - backgroundColor: Colors.white, - showIndicator: true, - isScrollable: true, - ), - Expanded(child: TDTabBarView( - children: _getTabViews(), - controller: _tabController, - )) - ], - ); - } - - List _getTabs() { - var tabs = const [ - TDTab(text: 'Api'), - TDTab(text: 'Code'), - ]; - return tabs; - } - - List _getTabViews() { - var tabViews = [ - ApiWidget(apiName: widget.apiPath,visible: true,), - CodeWidget(codePath: widget.codePath,), - ]; - return tabViews; - } -} - -class MobileWidget extends StatefulWidget { - const MobileWidget({Key? key,required this.page}) : super(key: key); - - final String page; - - @override - State createState() => _MobileWidgetState(); -} - -class _MobileWidgetState extends State { - - double screenWidth = 520; - double screenHeight = 1080; - String? lastPage; - - Widget? lastWidget; - - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - return getWebView(); - } - - Widget getWebView() { - if(widget.page == lastPage && lastWidget != null){ - return lastWidget!; - } - lastPage = widget.page; - var _iframeElement = html.IFrameElement(); - // ignore: unsafe_html - _iframeElement.src = _getSrc(); - _iframeElement.style.border = 'none'; - var id = 'iframeElement$lastPage'; -// ignore: undefined_prefixed_name - ui.platformViewRegistry.registerViewFactory( - id, - (int viewId) => _iframeElement, - ); - Widget _iframeWidget; - _iframeWidget = HtmlElementView( - key: UniqueKey(), - viewType: id, - ); - - - lastWidget = SizedBox( - width: screenWidth, - height: screenHeight, - child: Stack( - children: [ - IgnorePointer( - ignoring: true, - child: Center( - child: _iframeWidget, - ), - ), - ], - ), - ); - return lastWidget!; - } - - String? _getSrc() { - var href = html.window.location.href; - return href.replaceFirst('#/', '#$lastPage'); - } -} - - - -class WebApiController { - static _WebMainBodyState? _state; - - static setApiPath({required String? apiPath,required String? mobilePath, String? codePath}){ - _state?.setApiPath(apiPath: apiPath, mobilePath:mobilePath, codePath: codePath); - } -} \ No newline at end of file diff --git a/example/lib/web/web_replace.dart b/example/lib/web/web_replace.dart deleted file mode 100644 index dbbad266e..000000000 --- a/example/lib/web/web_replace.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; - -class WebMainBody extends StatelessWidget { - const WebMainBody({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container(); - } -} diff --git a/example/pubspec.lock b/example/pubspec.lock deleted file mode 100644 index 19bd263c1..000000000 --- a/example/pubspec.lock +++ /dev/null @@ -1,231 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - args: - dependency: transitive - description: - name: args - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.3.1" - async: - dependency: transitive - description: - name: async - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.9.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.1" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.3.1" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.16.0" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.5" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.3.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_easyrefresh: - dependency: transitive - description: - name: flutter_easyrefresh - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.2.2" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.4" - flutter_markdown: - dependency: "direct main" - description: - name: flutter_markdown - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.6.10" - flutter_swiper_null_safety: - dependency: "direct main" - description: - name: flutter_swiper_null_safety - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.2" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - http: - dependency: "direct main" - description: - name: http - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.13.5" - http_parser: - dependency: transitive - description: - name: http_parser - url: "https://pub.flutter-io.cn" - source: hosted - version: "4.0.2" - lints: - dependency: transitive - description: - name: lints - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.1" - markdown: - dependency: transitive - description: - name: markdown - url: "https://pub.flutter-io.cn" - source: hosted - version: "5.0.0" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.12.12" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.1.5" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.8.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.8.2" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.9.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.1" - tdesign_flutter: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "0.0.1" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.4.12" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.3.1" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.2" -sdks: - dart: ">=2.17.0-0 <3.0.0" - flutter: ">=1.24.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml deleted file mode 100644 index 79fd01aca..000000000 --- a/example/pubspec.yaml +++ /dev/null @@ -1,100 +0,0 @@ -name: tdesign_flutter_example -description: A new Flutter application. - -# The following line prevents the package from being accidentally published to -# pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 - -environment: - sdk: ">=2.12.0 <3.0.0" - -# Dependencies specify other packages that your package needs in order to work. -# To automatically upgrade your package dependencies to the latest versions -# consider running `flutter pub upgrade --major-versions`. Alternatively, -# dependencies can be manually updated by changing the version numbers below to -# the latest version available on pub.dev. To see which dependencies have newer -# versions available, run `flutter pub outdated`. -dependencies: - flutter: - sdk: flutter - - #轮播图组件 - flutter_swiper_null_safety: ^1.0.2 - - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 - flutter_markdown: ^0.6.1 - http: ^0.13.5 - tdesign_flutter: - path: ../ - -dev_dependencies: - flutter_test: - sdk: flutter - - # The "flutter_lints" package below contains a set of recommended lints to - # encourage good coding practices. The lint set provided by the package is - # activated in the `analysis_options.yaml` file located at the root of your - # package. See that file for information about deactivating specific lint - # rules and activating additional ones. - flutter_lints: ^1.0.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages - - assets: - - assets/api/ - - assets/version \ No newline at end of file diff --git a/example/shell/build_web.sh b/example/shell/build_web.sh deleted file mode 100644 index 2bf47ef84..000000000 --- a/example/shell/build_web.sh +++ /dev/null @@ -1,3 +0,0 @@ -# 构建web应用 -cd .. -flutter build web --web-renderer html --base-href /tdesign_example/ --source-maps \ No newline at end of file diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index d2638d8d5..000000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:tdesign_flutter_example/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/lib/src/components/avatar/td_avatar.dart b/lib/src/components/avatar/td_avatar.dart deleted file mode 100644 index cdbbd1b35..000000000 --- a/lib/src/components/avatar/td_avatar.dart +++ /dev/null @@ -1,306 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; - -enum TDAvatarSize { large, medium, small } - -enum TDAvatarType { - normal, - user, - circle, - square, - customText, - display, - operation -} - -/// 用于头像显示 -class TDAvatar extends StatelessWidget { - const TDAvatar( - {Key? key, - this.size = TDAvatarSize.medium, - this.type = TDAvatarType.normal, - this.text, - this.avatarUrl, - this.avatarSize, - this.avatarDisplayList, - this.displayText, - this.onTap, - this.defaultUrl = ''}) - : super(key: key); - - /// 头像地址 - final String? avatarUrl; - - /// 头像尺寸 - final TDAvatarSize size; - - /// 头像类型 - final TDAvatarType type; - - /// 自定义文字 - final String? text; - - /// 自定义头像大小 - final double? avatarSize; - - /// 默认图片 - final String defaultUrl; - - /// 带操作\展示的头像列表 - final List? avatarDisplayList; - - /// 纯展示类型末尾文字 - final String? displayText; - - /// 操作点击事件 - final Function()? onTap; - - - double _getAvatarWidth() { - double width; - switch (size) { - case TDAvatarSize.large: - width = 64; - break; - case TDAvatarSize.medium: - width = 48; - break; - case TDAvatarSize.small: - width = 32; - break; - } - return avatarSize ?? width; - } - - Font _getTextFont() { - Font font; - switch (size) { - case TDAvatarSize.large: - font = Font(size: 16, lineHeight: 24); - break; - case TDAvatarSize.medium: - font = Font(size: 14, lineHeight: 22); - break; - case TDAvatarSize.small: - font = Font(size: 12, lineHeight: 20); - break; - } - return font; - } - - double _getIconWidth() { - double width; - switch (size) { - case TDAvatarSize.large: - width = 32; - break; - case TDAvatarSize.medium: - width = 24; - break; - case TDAvatarSize.small: - width = 16; - break; - } - return width; - } - - @override - Widget build(BuildContext context) { - switch (type) { - case TDAvatarType.normal: - return CircleAvatar( - radius: _getAvatarWidth() / 2, - backgroundColor: TDTheme.of(context).brandColor2, - child: Icon(TDIcons.user, - size: _getIconWidth(), color: TDTheme.of(context).brandColor8), - ); - case TDAvatarType.user: - return CircleAvatar( - radius: _getAvatarWidth() / 2, - backgroundColor: TDTheme.of(context).brandColor2, - backgroundImage: avatarUrl != null - ? NetworkImage(avatarUrl!) - : NetworkImage(defaultUrl)); - case TDAvatarType.circle: - return CircleAvatar( - radius: _getAvatarWidth() / 2, - backgroundColor: TDTheme.of(context).brandColor2, - backgroundImage: avatarUrl != null - ? NetworkImage(avatarUrl!) - : NetworkImage(defaultUrl)); - case TDAvatarType.square: - return Container( - width: _getAvatarWidth(), - height: _getAvatarWidth(), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - image: DecorationImage( - image: avatarUrl != null - ? NetworkImage(avatarUrl!) - : NetworkImage(defaultUrl))), - ); - case TDAvatarType.customText: - return CircleAvatar( - radius: _getAvatarWidth() / 2, - backgroundColor: TDTheme.of(context).brandColor8, - child: TDText( - text, - forceVerticalCenter: true, - textAlign: TextAlign.center, - font: _getTextFont(), - textColor: TDTheme.of(context).whiteColor1, - ), - ); - case TDAvatarType.display: - return buildDisplayAvatar(context); - case TDAvatarType.operation: - return buildOperationAvatar(context); - } - } - - double _getDisplayPadding() { - double padding; - switch (size) { - case TDAvatarSize.large: - padding = 10; - break; - case TDAvatarSize.medium: - padding = 8; - break; - case TDAvatarSize.small: - padding = 6; - break; - } - return padding; - } - - Widget buildOperationAvatar(BuildContext context) { - var list = []; - if(avatarDisplayList == null || avatarDisplayList!.isEmpty) { - return Container(); - } - - for(var i = 0; i < avatarDisplayList!.length + 1; i ++) { - var left = (_getAvatarWidth() - _getDisplayPadding()) * i; - if(i == avatarDisplayList!.length) { - list.add( - Positioned( - left: left, - child: - GestureDetector( - onTap: () { - if(onTap != null) { - onTap!(); - } - }, - child: Container( - child: Center( - child: Icon(TDIcons.user_add, - size: _getIconWidth(), color: TDTheme.of(context).brandColor8), - ), - width: _getAvatarWidth(), - height: _getAvatarWidth(), - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - color: TDTheme.of(context).brandColor2, - borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), - border: Border.all(color: Colors.white, width: _getDisplayPadding() / 2) - ) - ), - ) - ) - ); - } else { - list.add( - Positioned( - left: left, - child: Container( - width: _getAvatarWidth(), - height: _getAvatarWidth(), - clipBehavior: Clip.antiAlias, - decoration: ShapeDecoration( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), - side: BorderSide(color: Colors.white, width: _getDisplayPadding() / 2) - ), - image: DecorationImage(image: NetworkImage(avatarDisplayList![i]), fit: BoxFit.cover) - ) - ) - ) - ); - } - } - return SizedBox( - height: _getAvatarWidth(), - width: _getAvatarWidth() * ((avatarDisplayList?.length ?? 0) + 1) - - (avatarDisplayList?.length ?? 0) * _getDisplayPadding(), - child: Stack( - children: list - ), - ); - } - - Widget buildDisplayAvatar(BuildContext context) { - var list = []; - if(avatarDisplayList == null || avatarDisplayList!.isEmpty) { - return Container(); - } - - for(var i = avatarDisplayList!.length; i >= 0; i --) { - var left = (_getAvatarWidth() - _getDisplayPadding()) * i; - if(i == avatarDisplayList!.length) { - list.add( - Positioned( - left: left, - child: - Container( - child: Center( - child: TDText( - displayText, - forceVerticalCenter: true, - textAlign: TextAlign.center, - font: _getTextFont(), - textColor: TDTheme.of(context).brandColor8, - ), - ), - width: _getAvatarWidth(), - height: _getAvatarWidth(), - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - color: TDTheme.of(context).brandColor2, - borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), - border: Border.all(color: Colors.white, width: _getDisplayPadding() / 2) - ) - ) - ) - ); - } else { - list.add( - Positioned( - left: left, - child: Container( - width: _getAvatarWidth(), - height: _getAvatarWidth(), - clipBehavior: Clip.antiAlias, - decoration: ShapeDecoration( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), - side: BorderSide(color: Colors.white, width: _getDisplayPadding() / 2) - ), - image: DecorationImage(image: NetworkImage(avatarDisplayList![i]), fit: BoxFit.cover) - ) - ) - ) - ); - } - } - return SizedBox( - height: _getAvatarWidth(), - width: _getAvatarWidth() * ((avatarDisplayList?.length ?? 0) + 1) - - (avatarDisplayList?.length ?? 0) * _getDisplayPadding(), - child: Stack( - children: list - ), - ); - } -} diff --git a/lib/src/components/badge/td_badge.dart b/lib/src/components/badge/td_badge.dart deleted file mode 100644 index a684b6ca4..000000000 --- a/lib/src/components/badge/td_badge.dart +++ /dev/null @@ -1,182 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; - -enum TDBadgeType { - /// 红点样式 - redPoint, - - /// 提醒样式 - remind, - - /// 消息样式 - message, - - /// 角标样式 - subscript -} - -enum TDBadgeBorder { - /// 大圆角 8px - large, - - /// 小圆角 4px - small -} - -class TDBadge extends StatefulWidget { - - const TDBadge( - this.type, { - Key? key, - this.count, - this.border = TDBadgeBorder.large, - this.color, - this.textColor, - this.redPointSize = 10, - this.message, - this.widthLarge = 32, - this.widthSmall = 12, - this.padding - }) : super(key: key); - - /// 红点数量 - final String? count; - - /// 红点样式 - final TDBadgeType type; - - /// 红点圆角大小 - final TDBadgeBorder border; - - /// 红点颜色 - final Color? color; - - /// 文字颜色 - final Color? textColor; - - /// 红点大小 - final double redPointSize; - - /// 消息内容 - final String? message; - - /// 角标大三角形宽 - final double widthLarge; - - /// 角标小三角形宽 - final double widthSmall; - - /// 角标自定义padding - final EdgeInsetsGeometry? padding; - - @override - State createState() => _TDBadgeState(); -} - -class _TDBadgeState extends State { - String badgeNum = ''; - - void updateBadgeNum(String? newCount) { - if (newCount == null) { - return; - } - setState(() { - badgeNum = newCount; - }); - } - - @override - Widget build(BuildContext context) { - updateBadgeNum(widget.count); - switch (widget.type) { - case TDBadgeType.redPoint: - return Container( - alignment: Alignment.center, - height: widget.redPointSize, - width: widget.redPointSize, - decoration: BoxDecoration( - color: widget.color ?? TDTheme.of(context).errorColor6, - borderRadius: BorderRadius.circular(widget.redPointSize / 2)), - ); - case TDBadgeType.remind: - return Container( - alignment: Alignment.center, - height: 16, - width: 20, - decoration: BoxDecoration( - color: widget.color ?? TDTheme.of(context).errorColor6, - borderRadius: BorderRadius.circular(8)), - child: Icon( - TDIcons.ellipsis, - color: widget.textColor ?? TDTheme.of(context).whiteColor1, - size: 12, - )); - case TDBadgeType.message: - return Container( - height: 16, - padding: const EdgeInsets.only(left: 5, right: 5), - decoration: BoxDecoration( - color: widget.color ?? TDTheme.of(context).errorColor6, - borderRadius: widget.border == TDBadgeBorder.large - ? BorderRadius.circular(8) - : BorderRadius.circular(4), - ), - child: TDText( - widget.message ?? '$badgeNum', - forceVerticalCenter: true, - font: Font(size: 10, lineHeight: 16), - fontWeight: FontWeight.w500, - textColor: widget.textColor ?? TDTheme.of(context).whiteColor1, - textAlign: TextAlign.center, - )); - case TDBadgeType.subscript: - return ClipPath( - clipper: TrapezoidPath(widget.widthLarge, widget.widthSmall), - child: Container( - alignment: Alignment.topRight, - color: widget.color ?? TDTheme.of(context).errorColor6, - height: 32, - width: 32, - child: Transform.rotate( - angle: pi / 4, - child: Padding( - padding: widget.padding ?? const EdgeInsets.only(left: 4, bottom: 8), - child: TDText( - widget.message ?? '$badgeNum', - font: Font(size: 10, lineHeight: 16), - fontWeight: FontWeight.w500, - textColor: widget.textColor ?? TDTheme.of(context).whiteColor1, - textAlign: TextAlign.center, - ), - ) - ), - ), - ); - } - } -} - -class TrapezoidPath extends CustomClipper { - final double widthLarge; - final double widthSmall; - TrapezoidPath(this.widthLarge, this.widthSmall); - @override - Path getClip(Size size) { - var path = Path(); - path.moveTo(0, 0); - path.lineTo(widthLarge - widthSmall, 0); - path.lineTo(widthLarge, widthSmall); - path.lineTo(widthLarge, widthLarge); - path.lineTo(0, 0); - return path; - } - - @override - bool shouldReclip(CustomClipper oldClipper) { - return true; - } -} \ No newline at end of file diff --git a/lib/src/components/bottom_nav_bar/td_bottom_nav_bar.dart b/lib/src/components/bottom_nav_bar/td_bottom_nav_bar.dart deleted file mode 100644 index f761f3d25..000000000 --- a/lib/src/components/bottom_nav_bar/td_bottom_nav_bar.dart +++ /dev/null @@ -1,670 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; - -// 向下箭头宽高 -const double _kArrowWidth = 13.5; -// 向下箭头宽高 -const double _kArrowHeight = 8; -// 选项弹窗 单个item最低高度 -const double _kMenuItemMinHeight = 23; -// 选项弹窗 单个item默认高度 -const double _kDefaultMenuItemHeight = 48; -// 选项弹窗 单个item默认宽度为按钮宽度-20 -const double _kDefaultMenuItemWidthShrink = 20; -// 底部弹窗默认高度 -const double _kDefaultNavBarHeight = 48; -// 弹窗弹出动画时间 -const Duration _kPopupMenuDuration = Duration(milliseconds: 10); - -enum TDBottomNavBarType { - /// 单层级纯文本标签栏 - text, - - /// 文本加图标标签栏 - iconText, - - /// 纯图标标签栏 - icon, - - /// 双层级纯文本标签栏 - expansionPanel, - - // 自定义布局 - customLayout, -} - -class TDBottomNavBarTabConfig { - /// 自定义未选中状态widget - final Widget? unSelectWidget; - - /// 自定义选中状态widget - final Widget? selectWidget; - - /// 图标 - final Widget? selectedIcon; - - /// 未选中图标 - final Widget? unselectedIcon; - - /// 选中文本 - final Text? selectedText; - - /// 未选中文本 - final Text? unselectedText; - - /// 点击事件 - final GestureTapCallback? onTap; - - /// 是否展示消息样式 - final bool? showBadge; - - /// 自定义消息样式 - final Widget? customBadgeWidget; - - /// 使用TDBadge消息样式 - final TDBadge? tdBadge; - - /// 消息顶部偏移量 - final double? badgeTopOffset; - - /// 消息右侧偏移量 - final double? badgeRightOffset; - - final TDBottomNavBarPopUpBtnConfig? popUpButtonConfig; - - TDBottomNavBarTabConfig({ - required this.onTap, - this.unSelectWidget, - this.selectWidget, - this.selectedIcon, - this.unselectedIcon, - this.selectedText, - this.unselectedText, - this.showBadge, - this.customBadgeWidget, - this.tdBadge, - this.badgeTopOffset, - this.badgeRightOffset, - this.popUpButtonConfig, - }) : assert(() { - if (showBadge != null && showBadge) { - if (customBadgeWidget == null && tdBadge == null) { - throw FlutterError('[NavigationTab] if set showBadge = true, ' - 'you must set customBadgeWidget or tdBadge'); - } - } - return true; - }()); -} - -class TDBottomNavBar extends StatefulWidget { - - TDBottomNavBar( - this.type, { - Key? key, - required this.navigationTabs, - this.barHeight = _kDefaultNavBarHeight, - this.useVerticalDivider, - this.dividerHeight, - this.dividerThickness, - this.dividerColor, - this.showTopBorder = true, - this.topBorder, - }) : assert(() { - if (navigationTabs.isEmpty) { - throw FlutterError( - '[TDBottomNavigationBar] please set at least one tab!'); - } - if (type == TDBottomNavBarType.customLayout) { - for (final item in navigationTabs) { - if (item.selectWidget == null || item.unSelectWidget == null) { - throw FlutterError( - '[TDBottomNavigationBar] customLayout request selectWidget and unSelectWidget,' - 'but get null.'); - } - } - } - if (type == TDBottomNavBarType.text) { - for (final item in navigationTabs) { - if (item.selectedText == null || item.unselectedText == null) { - throw FlutterError( - '[TDBottomNavigationBar] type is TDBottomBarType.text, but not set text.'); - } - } - } - if (type == TDBottomNavBarType.icon) { - for (final item in navigationTabs) { - if (item.selectedIcon == null || item.unselectedIcon == null) { - throw FlutterError( - '[TDBottomNavigationBar] type is TDBottomBarType.icon,' - 'but not set icon.'); - } - } - } - if (type == TDBottomNavBarType.iconText) { - for (final item in navigationTabs) { - if (item.selectedIcon == null || - item.unselectedIcon == null || - item.selectedText == null || - item.unselectedText == null) { - throw FlutterError( - '[TDBottomNavigationBar] type is TDBottomBarType.iconText,' - 'but not set icon or text.'); - } - } - } - return true; - }()), - super(key: key); - - final TDBottomNavBarType type; - - /// tab - final List navigationTabs; - - /// tab高度 - final double? barHeight; - - /// 是否使用竖线分隔 - final bool? useVerticalDivider; - - /// 分割线高度(可选) - final double? dividerHeight; - - /// 分割线厚度(可选) - final double? dividerThickness; - - /// 分割线颜色(可选) - final Color? dividerColor; - - /// 是否展示bar上边线(设置为true 但是topBorder样式未设置,则使用默认值) - final bool? showTopBorder; - - /// 上边线样式 - final BorderSide? topBorder; - - @override - State createState() => _TDBottomNavBarState(); -} - -class _TDBottomNavBarState extends State { - int selectedIndex = 0; - - @override - Widget build(BuildContext context) { - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - var maxWidth = - double.parse(constraints.biggest.width.toStringAsFixed(1)); - var itemWidth = maxWidth / widget.navigationTabs.length; - return Container( - decoration: widget.showTopBorder! - ? BoxDecoration( - border: Border( - top: widget.topBorder ?? - BorderSide( - width: 0.5, - color: TDTheme.of(context).grayColor3))) - : null, - child: Stack(alignment: Alignment.center, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: - List.generate(widget.navigationTabs.length, (index) { - return _item(index, itemWidth); - })), - _verticalDivider(), - ])); - }, - ); - } - - void _onTap(int index) { - setState(() { - if (selectedIndex != index) { - selectedIndex = index; - widget.navigationTabs[index].onTap?.call(); - } - }); - } - - Widget _item(int index, double itemWidth) { - return Container( - height: widget.barHeight ?? _kDefaultNavBarHeight, - width: itemWidth, - alignment: Alignment.center, - child: TDBottomNavBarItemWithBadge( - item: _constructItem(index), - itemHeight: widget.barHeight ?? _kDefaultNavBarHeight, - itemWidth: itemWidth, - onTap: () { - _onTap(index); - }, - showBage: widget.navigationTabs[index].showBadge ?? false, - badge: _badge(index), - rightOffset: widget.navigationTabs[index].badgeRightOffset, - topOffset: widget.navigationTabs[index].badgeTopOffset, - popUpButtonConfig: widget.navigationTabs[index].popUpButtonConfig, - )); - } - - Widget _constructItem(int index) { - if (widget.type == TDBottomNavBarType.customLayout) { - return index == selectedIndex - ? widget.navigationTabs[index].selectWidget! - : widget.navigationTabs[index].unSelectWidget!; - } - if (widget.type == TDBottomNavBarType.text) { - return index == selectedIndex - ? widget.navigationTabs[index].selectedText! - : widget.navigationTabs[index].unselectedText!; - } - if (widget.type == TDBottomNavBarType.icon) { - return index == selectedIndex - ? widget.navigationTabs[index].selectedIcon! - : widget.navigationTabs[index].unselectedIcon!; - } - - if (widget.type == TDBottomNavBarType.iconText) { - return index == selectedIndex - ? Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - widget.navigationTabs[index].selectedIcon!, - widget.navigationTabs[index].selectedText! - ], - ) - : Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - widget.navigationTabs[index].unselectedIcon!, - widget.navigationTabs[index].unselectedText! - ], - ); - } - return Container(); - } - - Widget _badge(int index) { - if (widget.navigationTabs[index].showBadge ?? false) { - if (widget.navigationTabs[index].tdBadge != null) { - return widget.navigationTabs[index].tdBadge!; - } else if (widget.navigationTabs[index].customBadgeWidget != null) { - return widget.navigationTabs[index].customBadgeWidget!; - } - } - return Container(); - } - - Widget _verticalDivider() { - return Visibility( - visible: widget.useVerticalDivider ?? false, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: List.generate(widget.navigationTabs.length - 1, (index) { - return SizedBox( - width: widget.dividerThickness ?? 0.5, - height: widget.dividerHeight ?? 32, - child: VerticalDivider( - color: widget.dividerColor ?? TDTheme.of(context).grayColor3, - thickness: widget.dividerThickness ?? 0.5, - ), - ); - }), - ), - ); - } -} - -class TDBottomNavBarItemWithBadge extends StatelessWidget { - final Widget item; - final double itemHeight; - final double itemWidth; - final GestureTapCallback onTap; - final bool showBage; - final Widget badge; - final double? rightOffset; - final double? topOffset; - final TDBottomNavBarPopUpBtnConfig? popUpButtonConfig; - - const TDBottomNavBarItemWithBadge({ - Key? key, - required this.item, - required this.itemHeight, - required this.itemWidth, - required this.onTap, - required this.showBage, - required this.badge, - this.rightOffset, - this.topOffset, - this.popUpButtonConfig, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - onTap.call(); - if (popUpButtonConfig != null) { - Navigator.push( - context, - PopRoute( - child: PopupDialog( - itemWidth - _kDefaultMenuItemWidthShrink, - btnContext: context, - config: popUpButtonConfig!.popUpDialogConfig, - items: popUpButtonConfig!.items, - onClickMenu: (value) { - popUpButtonConfig!.onChanged(value); - }, - ), - )); - } - }, - child: Container( - height: itemHeight, - alignment: Alignment.center, - child: Stack( - alignment: Alignment.center, - clipBehavior: Clip.none, - children: [ - item, - Visibility( - visible: showBage, - child: Positioned( - top: topOffset ?? -5, - right: rightOffset ?? -10, - child: badge), - ) - ], - ), - ), - ); - } -} - -class TDBottomNavBarPopUpBtnConfig { - // 选项list - final List items; - - // 统一在 onChanged 中处理各item点击事件 - final ValueChanged onChanged; - - final TDBottomNavBarPopUpShapeConfig? popUpDialogConfig; - - TDBottomNavBarPopUpBtnConfig( - {required this.items, required this.onChanged, this.popUpDialogConfig}) - : assert(() { - if (popUpDialogConfig != null) { - if ((popUpDialogConfig.arrowHeight != null && - popUpDialogConfig.arrowHeight! <= 0.0) || - (popUpDialogConfig.arrowWidth != null && - popUpDialogConfig.arrowWidth! <= 0.0)) { - throw FlutterError( - '[TDBottomNavBarPopUpBtnConfig] arrowHeight or arrowHeight can ' - 'not set less than or equal to zero'); - } - } - return true; - }()); -} - -class TDBottomNavBarPopUpShapeConfig { - // 弹窗宽度(不设置,默认为按钮宽度 - 20) - final double? popUpWidth; - - // 单个选项高度 所有选项等高 不设置则使用默认值 48 - final double? popUpitemHeight; - - // 弹窗背景颜色 - Color? backgroundColor; - - /// pannel圆角 默认0 - double? radius; - - /// 箭头宽度 默认13.5 - double? arrowWidth; - - // 箭头高度 默认8 - double? arrowHeight; - - TDBottomNavBarPopUpShapeConfig( - {this.popUpWidth, - this.popUpitemHeight = _kDefaultMenuItemHeight, - this.backgroundColor, - this.radius, - this.arrowWidth, - this.arrowHeight}); -} - -class PopUpMenuItem extends StatelessWidget { - // 选项widget - final Widget? itemWidget; - - // 选项值 - final String value; - - // 对齐方式 - final AlignmentGeometry alignment; - - const PopUpMenuItem({ - Key? key, - this.itemWidget, - required this.value, - this.alignment = AlignmentDirectional.center, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Container( - constraints: const BoxConstraints(minHeight: _kMenuItemMinHeight), - alignment: alignment, - child: itemWidget ?? - Text( - value, - style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w400), - ), - ); - } -} - -class PopRoute extends PopupRoute { - Widget child; - - PopRoute({required this.child}); - - @override - Color? get barrierColor => Colors.transparent; - - @override - bool get barrierDismissible => true; - - @override - String? get barrierLabel => 'popUpMenuBarrierLabel'; - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return child; - } - - @override - Duration get transitionDuration => _kPopupMenuDuration; -} - -class PopupDialog extends StatefulWidget { - // 按钮context - final BuildContext btnContext; - - // 点击事件 - final ValueChanged onClickMenu; - - // 弹窗选项列表 - final List items; - - // 弹窗配置 - final TDBottomNavBarPopUpShapeConfig? config; - - // 默认弹窗宽度 - final double defaultPopUpWidth; - - const PopupDialog(this.defaultPopUpWidth, - {Key? key, - required this.btnContext, - required this.onClickMenu, - required this.items, - required this.config}) - : super(key: key); - - @override - PopupDialogState createState() => PopupDialogState(); -} - -class PopupDialogState extends State { - RenderBox? button; - RenderBox? overlay; - RelativeRect? position; - Size? size; - - @override - void initState() { - super.initState(); - button = widget.btnContext.findRenderObject() as RenderBox; - size = button!.size; - overlay = - Overlay.of(widget.btnContext)?.context.findRenderObject() as RenderBox; - position = RelativeRect.fromRect( - Rect.fromPoints( - button!.localToGlobal(Offset.zero, ancestor: overlay), - button!.localToGlobal(Offset.zero, ancestor: overlay), - ), - Offset.zero & overlay!.size, - ); - } - - @override - Widget build(BuildContext context) { - var popUpitemHeight = - widget.config?.popUpitemHeight ?? _kDefaultMenuItemHeight; - var popUpItemWidth = widget.config?.popUpWidth ?? widget.defaultPopUpWidth; - var menuItems = widget.items - .map((e) => GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - widget.onClickMenu(e.value); - Navigator.of(context).pop(); - }, - child: SizedBox( - height: popUpitemHeight, - child: e, - ))) - .toList(); - - return Material( - type: MaterialType.transparency, - child: GestureDetector( - onTap: () => Navigator.of(context).pop(), - child: Stack( - children: [ - Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - color: Colors.transparent, - ), - Positioned( - top: position!.top - - (popUpitemHeight * widget.items.length + - (widget.config?.arrowHeight ?? _kArrowHeight)), - right: position!.right - (popUpItemWidth + size!.width) / 2, - child: SizedBox( - width: popUpItemWidth, - height: popUpitemHeight * widget.items.length + - (widget.config?.arrowHeight ?? _kArrowHeight), - child: CustomPaint( - painter: PannelWithDownArrow(config: widget.config), - child: Container( - alignment: Alignment.topCenter, - height: popUpitemHeight * widget.items.length, - child: Container( - constraints: BoxConstraints( - maxHeight: popUpitemHeight * widget.items.length), - child: Stack( - children: [ - Column(children: menuItems), - Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: List.generate( - widget.items.length - 1, - (index) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0), - child: Divider( - thickness: 0.5, - height: 0.5, - color: TDTheme.of(context).grayColor3, - ), - )), - ) - ], - ), - ), - ), - ), - )) - ], - ), - ), - ); - } -} - -/// 带下箭头的展开pannel -class PannelWithDownArrow extends CustomPainter { - TDBottomNavBarPopUpShapeConfig? config; - - PannelWithDownArrow({ - this.config, - }); - - @override - void paint(Canvas canvas, Size size) { - var paint = Paint() - ..isAntiAlias = true - ..color = config?.backgroundColor ?? Colors.white - ..style = PaintingStyle.fill; - var path = Path(); - var pannelWidth = size.width; - var pannelHeight = size.height - (config?.arrowHeight ?? _kArrowHeight); - - canvas.drawRRect( - RRect.fromRectAndRadius(Rect.fromLTWH(0, 0, pannelWidth, pannelHeight), - Radius.circular(config?.radius ?? 0.0)), - paint); - - // 下方箭头 - if (config?.arrowWidth != 0.0 && config?.arrowHeight != 0.0) { - var left = (pannelWidth - _kArrowWidth) / 2; - var right = (pannelWidth + _kArrowWidth) / 2; - var bottom = pannelHeight + _kArrowHeight; - if (config?.arrowWidth != null) { - left = (pannelWidth - config!.arrowWidth!) / 2; - right = (pannelWidth + config!.arrowWidth!) / 2; - } - if (config?.arrowHeight != null) { - bottom = pannelHeight + config!.arrowHeight!; - } - - path.moveTo(left, pannelHeight); - path.lineTo(pannelWidth / 2, bottom); - path.lineTo(right, pannelHeight); - canvas.drawPath(path, paint); - } - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return false; - } -} diff --git a/lib/src/components/button/td_button.dart b/lib/src/components/button/td_button.dart deleted file mode 100644 index b6afad369..000000000 --- a/lib/src/components/button/td_button.dart +++ /dev/null @@ -1,304 +0,0 @@ - -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; - -enum TDButtonSize { small, medium, large } - -typedef TDButtonEvent = void Function(); - -/// TD常规按钮 -class TDButton extends StatefulWidget { - - const TDButton( - {Key? key, - required this.content, - this.size = TDButtonSize.medium, - this.child, - this.disabled = false, - this.style, - this.textStyle, - this.disableTextStyle, - this.width, - this.height, - this.onTap, - this.icon, - this.onLongPress, - this.padding}) - : super(key: key); - - /// 自控件 - final Widget? child; - /// 文本内容 - final String? content; - /// 禁止点击 - final bool disabled; - /// 自定义宽度 - final double? width; - /// 自定义高度 - final double? height; - /// 尺寸 - final TDButtonSize size; - /// 样式,强按钮,弱按钮,警告按钮等 - final TDButtonStyle? style; - /// 自定义可点击状态文本样式 - final TextStyle? textStyle; - /// 自定义不可点击状态文本样式 - final TextStyle? disableTextStyle; - /// 点击事件 - final TDButtonEvent? onTap; - /// 长按事件 - final TDButtonEvent? onLongPress; - /// 图标icon - final IconData? icon; - /// 自定义padding - final EdgeInsetsGeometry? padding; - - @override - State createState() => _TDButtonState(); -} - -class _TDButtonState extends State { - - TDButtonStyle? _innerStyle; - - TDButtonStyle get style { - if(_innerStyle != null){ - return _innerStyle!; - } - _innerStyle = widget.style ?? TDButtonStyle.primary(context: context); - return _innerStyle!; - } - - @override - void initState() { - super.initState(); - } - - @override - Widget build(BuildContext context) { - var width = widget.width ?? _getWidth(); - - Widget display = Container( - width: width, - height: widget.height ?? _getHeight(), - alignment: style.isFullWidth ? Alignment.center : null, - padding: _getPadding(), - decoration: BoxDecoration( - color: style.getBackgroundColor(context:context, disable: widget.disabled), - border: _getBorder(context), - borderRadius: BorderRadius.all(style.radius ?? Radius.zero), - ), - child: widget.child ?? _getChild(), - ); - - if(widget.disabled){ - return display; - } - return GestureDetector( - child: display, - onTap: widget.onTap, - onLongPress: widget.onLongPress, - ); - } - - Border? _getBorder(BuildContext context) { - if( style.frameWidth != null && style.frameWidth != 0){ - return Border.all(color: style.getFrameColor(context:context, disable: widget.disabled), width: style.frameWidth!); - } - } - - Widget _getChild() { - if(widget.content == null && widget.icon == null){ - return Container(); - } - var children = []; - // 系统Icon会导致不居中,因此自绘icon指定height - if (widget.icon != null) { - var icon = RichText( - overflow: TextOverflow.visible, - text: TextSpan( - text: String.fromCharCode(widget.icon!.codePoint), - style: TextStyle( - inherit: false, - color: style.getTextColor(context: context, disable: widget.disabled), - height: 1, - fontSize: _getIconSize(), - fontFamily:widget.icon!.fontFamily, - package: widget.icon!.fontPackage, - ), - ), - ); - children.add(icon); - } - if(widget.content != null){ - var text = TDText(widget.content!, - font: _getTextFont(), - textColor: style.getTextColor(context: context, disable: widget.disabled), - style: widget.disabled ? widget.disableTextStyle : widget.textStyle, - forceVerticalCenter: true, - ); - children.add(text); - } - - if(children.length == 2){ - children.insert(1, const SizedBox(width: 8,),); - } - return Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: children, - ); - } - - Font _getTextFont() { - switch(widget.size){ - case TDButtonSize.large: - return TDTheme.of(context).fontM ?? Font(size: 16, lineHeight: 24); - case TDButtonSize.medium: - return TDTheme.of(context).fontS ?? Font(size: 14, lineHeight: 22); - case TDButtonSize.small: - return TDTheme.of(context).fontS ?? Font(size: 14, lineHeight: 22); - } - } - - double? _getWidth() { - if(!style.isFullWidth){ - switch(widget.size){ - case TDButtonSize.large: - return 343; - case TDButtonSize.medium: - return 343; - case TDButtonSize.small: - default: - } - } - } - - double _getHeight() { - switch(widget.size){ - case TDButtonSize.large: - return 44; - case TDButtonSize.medium: - return 40; - case TDButtonSize.small: - return 36; - } - } - - double _getIconSize() { - switch(widget.size){ - case TDButtonSize.large: - return 24; - case TDButtonSize.medium: - return 22; - case TDButtonSize.small: - return 20; - } - } - - - EdgeInsetsGeometry? _getPadding() { - if(widget.padding != null){ - return widget.padding; - } - double topBottomPadding; - switch(widget.size){ - case TDButtonSize.large: - topBottomPadding = 10; - break; - case TDButtonSize.medium: - topBottomPadding = 9; - break; - case TDButtonSize.small: - topBottomPadding = 7; - break; - } - return EdgeInsets.only(left: 16,right: 16,bottom: topBottomPadding,top: topBottomPadding); - } - - @override - void didUpdateWidget(covariant TDButton oldWidget) { - super.didUpdateWidget(oldWidget); - _innerStyle = widget.style ?? _innerStyle ; - } - -} - -/// TD文字按钮 -class TDTextButton extends StatefulWidget { - final TDButtonSize size; - final String? content; - final bool disabled; - final double? width; - final double? height; - final TextStyle? style; - final TextStyle? disableStyle; - final TDButtonEvent? onTap; - final TDButtonEvent? onLongPress; - - const TDTextButton( - this.content,{ - Key? key, - this.size = TDButtonSize.medium, - this.disabled = false, - this.style, - this.disableStyle, - this.width, - this.height, - this.onTap, - this.onLongPress -}) : super(key: key); - - @override - State createState() => _TDTextButtonState(); -} - -class _TDTextButtonState extends State { - - - late TextStyle? style; - late TextStyle? disableStyle; - - @override - void initState() { - super.initState(); - style = widget.style ?? TextStyle(color: TDTheme.of().brandNormalColor); - disableStyle = widget.disableStyle ?? TextStyle(color: TDTheme.of().brandDisabledColor); - } - - @override - Widget build(BuildContext context) { - - Widget display = SizedBox( - width: widget.width, - height: widget.height , - child: TDText(widget.content!, - font: _getTextFont(), - style: widget.disabled ? disableStyle : style, - forceVerticalCenter: true, - ), - ); - - if(widget.disabled){ - return display; - } - return GestureDetector( - child: display, - onTap: widget.onTap, - onLongPress: widget.onLongPress, - ); - } - - Font _getTextFont() { - switch(widget.size){ - case TDButtonSize.large: - return TDTheme.of(context).fontM ?? Font(size: 16, lineHeight: 24); - case TDButtonSize.medium: - return TDTheme.of(context).fontS ?? Font(size: 14, lineHeight: 22); - case TDButtonSize.small: - return TDTheme.of(context).fontS ?? Font(size: 14, lineHeight: 22); - } - } -} - diff --git a/lib/src/components/button/td_button_style.dart b/lib/src/components/button/td_button_style.dart deleted file mode 100644 index bd04a681b..000000000 --- a/lib/src/components/button/td_button_style.dart +++ /dev/null @@ -1,144 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; - -/// TDButton按钮样式 -class TDButtonStyle { - Color? backgroundColor; - Color? frameColor; - Color? textColor; - Color? disableBackgroundColor; - Color? disableFrameColor; - Color? disableTextColor; - Radius? radius; - double? frameWidth; - bool isFullWidth = false; - - Color getBackgroundColor({BuildContext? context, bool disable = false}){ - return (disable ? disableBackgroundColor : backgroundColor) ?? TDTheme.of(context).brandNormalColor; - } - - Color getFrameColor({BuildContext? context, bool disable = false}){ - return (disable ? disableFrameColor : frameColor) ?? TDTheme.of(context).brandNormalColor; - } - - Color getTextColor({BuildContext? context, bool disable = false}){ - return (disable ? disableTextColor : textColor) ?? TDTheme.of(context).fontWhColor1; - } - - TDButtonStyle( - {this.backgroundColor, - this.frameColor, - this.textColor, - this.disableBackgroundColor, - this.disableFrameColor, - this.disableTextColor, - this.frameWidth, - this.radius}); - - TDButtonStyle.primary({BuildContext? context}){ - var themeData = TDTheme.of(context); - backgroundColor = themeData.brandNormalColor; - textColor = themeData.fontWhColor1; - frameColor = backgroundColor; - disableBackgroundColor = themeData.brandDisabledColor; - disableTextColor = themeData.fontWhColor1; - disableFrameColor = disableBackgroundColor; - radius = const Radius.circular(4); - } - - TDButtonStyle.weakly({BuildContext? context}){ - var themeData = TDTheme.of(context); - textColor = themeData.brandNormalColor; - frameColor = themeData.brandNormalColor; - backgroundColor = themeData.whiteColor1; - disableTextColor = themeData.brandDisabledColor; - disableFrameColor = themeData.brandDisabledColor; - disableBackgroundColor = themeData.whiteColor1; - radius = const Radius.circular(4); - frameWidth = 0.5; - } - - TDButtonStyle.secondary({BuildContext? context}){ - var themeData = TDTheme.of(context); - disableTextColor = themeData.fontGyColor4; - disableFrameColor = themeData.grayColor4; - textColor = themeData.fontGyColor1; - frameColor = themeData.grayColor4; - backgroundColor = themeData.whiteColor1; - disableBackgroundColor = themeData.whiteColor1; - radius = const Radius.circular(4); - frameWidth = 0.5; - } - - TDButtonStyle.warningPrimary({BuildContext? context}){ - var themeData = TDTheme.of(context); - disableBackgroundColor = themeData.errorDisabledColor; - disableTextColor = themeData.fontWhColor1; - backgroundColor = themeData.errorNormalColor; - textColor = themeData.fontWhColor1; - frameColor = backgroundColor; - disableFrameColor = backgroundColor; - radius = const Radius.circular(4); - } - - TDButtonStyle.warningWeakly({BuildContext? context}){ - var themeData = TDTheme.of(context); - disableTextColor = themeData.errorDisabledColor; - disableFrameColor = themeData.errorDisabledColor; - textColor = themeData.errorNormalColor; - frameColor = themeData.errorNormalColor; - backgroundColor = themeData.whiteColor1; - disableBackgroundColor = themeData.whiteColor1; - radius = const Radius.circular(4); - frameWidth = 0.5; - } - - TDButtonStyle.ghost({BuildContext? context}){ - var themeData = TDTheme.of(context); - textColor = themeData.fontWhColor1; - frameColor = themeData.whiteColor1; - backgroundColor = Colors.transparent; - disableTextColor = themeData.fontWhColor3; - disableFrameColor = themeData.fontWhColor3; - disableBackgroundColor = Colors.transparent; - radius = const Radius.circular(4); - frameWidth = 0.5; - } - - TDButtonStyle.text({BuildContext? context, - Color? textColor, - Color? disableTextColor}){ - this.textColor = textColor ?? TDTheme.of(context).brandNormalColor; - this.disableTextColor = disableTextColor ?? TDTheme.of(context).brandDisabledColor; - frameColor = Colors.transparent; - backgroundColor = Colors.transparent; - disableFrameColor = Colors.transparent; - disableBackgroundColor = Colors.transparent; - radius = const Radius.circular(4); - } - - TDButtonStyle.full({BuildContext? context}){ - var themeData = TDTheme.of(context); - textColor = themeData.fontWhColor1; - frameColor = themeData.brandNormalColor; - backgroundColor = themeData.brandNormalColor; - disableTextColor = themeData.fontWhColor1; - disableFrameColor = themeData.brandDisabledColor; - disableBackgroundColor = themeData.brandDisabledColor; - radius = const Radius.circular(0); - isFullWidth = true; - } - - TDButtonStyle.fullSecondary({BuildContext? context}){ - var themeData = TDTheme.of(context); - textColor = themeData.fontGyColor1; - frameColor = themeData.whiteColor1; - backgroundColor = themeData.whiteColor1; - disableTextColor = themeData.fontGyColor4; - disableFrameColor = themeData.whiteColor1; - disableBackgroundColor = themeData.whiteColor1; - radius = const Radius.circular(0); - isFullWidth = true; - } -} \ No newline at end of file diff --git a/lib/src/components/checkbox/td_check_box.dart b/lib/src/components/checkbox/td_check_box.dart deleted file mode 100644 index baed15ca4..000000000 --- a/lib/src/components/checkbox/td_check_box.dart +++ /dev/null @@ -1,395 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -import '../../theme/td_colors.dart'; -import '../../theme/td_fonts.dart'; -import '../../theme/td_theme.dart'; -import '../divider/td_divider.dart'; -import '../icon/td_icons.dart'; -import '../text/td_text.dart'; -import 'td_check_box_group.dart'; - -/// -/// 选择框的样式 -/// -enum TDCheckboxStyle { - circle, // 圆形 - square, // 方形 -} - -/// -/// 内容相对icon的位置,上、下、左、右,默认内容在icon的右边 -/// -enum TDContentDirection { - left, // content在icon的左边 - right, // content在icon的右边 -} - -enum TDCheckBoxSize { - large, // 大 高度56 - small, // 小 高度48 -} - -/// -/// 自定义Icon -/// -typedef IconBuilder = Widget? Function(BuildContext context, bool checked); - -/// -/// 自定义Content -/// -typedef ContentBuilder = Widget Function(BuildContext context, bool checked, String? content); - - -typedef OnCheckValueChanged = void Function(bool selected); - -/// -/// 复选框组件。 -/// -/// FuiCheckbox支持3种内置样式的的复选框,还支持各种自定义样式,除了提供勾选之外还提供了内 -/// 容选项,内容包含一个主标题和副标题,并且支持完全自定义内容,支持指定内容的方向等等 -/// -/// -class TDCheckbox extends StatefulWidget { - - const TDCheckbox( - {this.id, - Key? key, - this.title, - this.subTitle, - this.enable = true, - this.checked = false, - this.titleMaxLine, - this.subTitleMaxLine = 1, - this.customIconBuilder, - this.customContentBuilder, - this.style, - this.spacing, - this.backgroundColor, - this.size = TDCheckBoxSize.small, - this.contentDirection = TDContentDirection.right, - this.onCheckBoxChanged}): super(key: key); - - /// id - /// 当FuiCheckBox嵌入到FuiCheckBoxGroup内时,这个值需要赋值,否则不会被纳入Group管理 - final String? id; - - /// 文本 - final String? title; - - /// 辅助文字 - final String? subTitle; - - /// 不可用 - final bool enable; - - /// 选中状态。默认为`false` - /// 当FuiCheckBox嵌入到FuiCheckBoxGroup的时候,这个值表示初始状态,后续的状态会由Group管理 - final bool checked; - - /// 标题的行数 - final int? titleMaxLine; - - /// 辅助文字的行数 - final int? subTitleMaxLine; - - /// icon和文字的距离 - final double? spacing; - - /// 复选框样式:圆形或方形 - final TDCheckboxStyle? style; - - /// 复选框大小 - final TDCheckBoxSize size; - - /// 文字相对icon的方位 - final TDContentDirection contentDirection; - - /// 切换监听 - final OnCheckValueChanged? onCheckBoxChanged; - - /// 自定义Checkbox显示样式 - final IconBuilder? customIconBuilder; - - /// 完全自定义内容 - final ContentBuilder? customContentBuilder; - - /// 背景颜色 - final Color? backgroundColor; - - @override - State createState() => TDCheckboxState(); - - /// 默认的checkBox icon - Widget buildDefaultIcon(BuildContext context, TDCheckboxGroupState? groupState, bool isChecked) { - Widget current; - var size = 24.0; - final style = this.style ?? groupState?.widget.style ?? TDCheckboxStyle.circle; - final theme = TDTheme.of(context); - current = Icon( - style == TDCheckboxStyle.circle - ? isChecked ? TDIcons.check_circle_filled : TDIcons.circle - : isChecked ? TDIcons.check_rectangle_filled : TDIcons.rectangle, - size: size, - color: isChecked && enable ? theme.brandColor8 : theme.grayColor4 - ); - return current; - } -} - -class TDCheckboxState extends State { - bool checked = false; - bool _pressed = false; - - /// 不可取消勾选,在radioButton的严格模式下,只能切换不能取消勾选 - bool canNotCancel = false; - - @override - void initState() { - checked = widget.checked; - super.initState(); - } - - @override - void didUpdateWidget(TDCheckbox oldWidget) { - checked = widget.checked; - super.didUpdateWidget(oldWidget); - } - - double _spacing(TDCheckboxGroupState? groupState) { - return widget.spacing ?? groupState?.widget.spacing ?? 8; - } - - EdgeInsets _getPadding(TDCheckBoxSize size) { - switch(size) { - case TDCheckBoxSize.small: - return const EdgeInsets.only(top: 12, bottom: 12); - case TDCheckBoxSize.large: - return const EdgeInsets.only(top: 16, bottom: 16); - } - } - - @override - Widget build(BuildContext context) { - // 检查是否包含在FuiCheckBoxGroup内,如果是的话,状态由Group管理 - final groupState = TDCheckboxGroupInherited.of(context)?.state; - final id = widget.id; - // 只有设置了id的CheckBox才会纳入Group管理 - if (groupState != null && id != null) { - // CheckBox嵌入在CheckBoxGroup的勾选状态的获取优先级: - // 1.CheckBoxGroup里设置的checkedIds包含当前id - // 2.没有checkedIds属性,且没有勾选过,使用当前CheckedBox设置的checked属性 - // 3.用户勾选之后的状态 - checked = groupState.getCheckBoxStateById(id, checked); - } - - // 构建icon - var icon = _buildCheckboxIcon(context, groupState, checked); - - // 内容 - var content = _buildContent(context, groupState, checked); - - - if (icon == null && content == null) { - throw Exception('Icon and content cannot both be null!'); - } - - Widget? current ; - - if (icon == null) { - current = content!; - } else { - - current = icon; - - if (content != null) { - final spacing = _spacing(groupState); - var contentDirection = groupState?.widget.contentDirection ?? widget.contentDirection; - switch (contentDirection) { - case TDContentDirection.left: - current = Stack( - alignment: Alignment.bottomCenter, - children: [ - Padding( - padding: _getPadding(widget.size), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded(child: Padding( - padding: const EdgeInsets.only(left: 16), - child: content, - )), - SizedBox(width: spacing,), - Padding( - padding: const EdgeInsets.only(right: 16), - child: icon, - ), - ], - ), - Visibility( - visible: widget.subTitle != null && widget.subTitle != '', - child: Padding( - padding: const EdgeInsets.only(left: 16, right: 16), - child: TDText( - widget.subTitle ?? '', - maxLines: - widget.subTitleMaxLine, - overflow: TextOverflow.ellipsis, - textColor: widget.enable ? TDTheme.of(context).fontGyColor3 : TDTheme.of(context).fontGyColor4, - font:TDTheme.of(context).fontS), - ), - ) - ], - ), - ), - const TDDivider(margin: EdgeInsets.only(left: 16),) - ], - ); - break; - case TDContentDirection.right: - current = Stack( - alignment: Alignment.bottomCenter, - children: [ - Padding( - padding: _getPadding(widget.size), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(left: 16), - child: icon, - ), - SizedBox(width: spacing,), - Expanded(child: Padding( - padding: const EdgeInsets.only(right: 16), - child: content, - )), - ], - ), - Visibility( - visible: widget.subTitle != null && widget.subTitle != '', - child: Padding( - padding: const EdgeInsets.only(left: 48, right: 16), - child: TDText( - widget.subTitle ?? '', - maxLines: - widget.subTitleMaxLine, - overflow: TextOverflow.ellipsis, - textColor: widget.enable ? TDTheme.of(context).fontGyColor3 : TDTheme.of(context).fontGyColor4, - font:TDTheme.of(context).fontS), - ), - ) - ], - ), - ), - const TDDivider(margin: EdgeInsets.only(left: 48),) - ], - ); - break; - } - } - } - - if (!(canNotCancel && checked)) { - if (_pressed) { - // 点击效果 - current = Opacity( - opacity: 0.68, - child: current, - ); - } - - // 开关样式的话自己会处理点击事件 - current = GestureDetector( - behavior: HitTestBehavior.translucent, - onTapDown: (detail) { - _pressState(true); - }, - onTapUp: (detail) { - _pressState(false); - }, - onTapCancel: () { - _pressState(false); - }, - onTap: () { - onValueChange(id, !checked, groupState); - }, - child: current, - ); - } - - return Container(child: current, color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1,); - } - - /// 点击效果 - void _pressState(bool pressed) { - if(!widget.enable) { - return; - } - _pressed = pressed; - setState(() {}); - } - - /// 选中状态的变化 - void onValueChange( - String? id, - bool value, - TDCheckboxGroupState? groupState, - ) { - if(!widget.enable) { - return; - } - setState(() { - checked = value; - if (groupState != null && id != null) { - groupState.toggle(id, checked); - } - widget.onCheckBoxChanged?.call(checked); - }); - } - - /// - /// 构建选择框边上的文本内容 - /// - Widget? _buildContent( - BuildContext context, - TDCheckboxGroupState? groupState, - bool checked, - ) { - - final title = widget.title; - final customContent = - widget.customContentBuilder ?? groupState?.widget.customContentBuilder; - - var content = customContent?.call(context, checked, title); - if (content == null) { - if (title != null || customContent != null && title != null) { - content = TDText( - title, - maxLines: - widget.titleMaxLine ?? groupState?.widget.titleMaxLine, - overflow: TextOverflow.ellipsis, - textColor: widget.enable ? TDTheme.of(context).fontGyColor1 : TDTheme.of(context).fontGyColor4, - font:TDTheme.of(context).fontM); - } - } - return content; - } - - /// 构建icon - Widget? _buildCheckboxIcon(BuildContext context, TDCheckboxGroupState? groupState, bool isCheck) { - final iconBuilder = widget.customIconBuilder ?? groupState?.widget.customIconBuilder; - if (iconBuilder != null) { - return iconBuilder.call(context, isCheck); - } - return widget.buildDefaultIcon(context, groupState, isCheck); - } -} diff --git a/lib/src/components/checkbox/td_check_box_group.dart b/lib/src/components/checkbox/td_check_box_group.dart deleted file mode 100644 index 8fd7d1ebd..000000000 --- a/lib/src/components/checkbox/td_check_box_group.dart +++ /dev/null @@ -1,293 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -import '../../util/map_ext.dart'; -import 'td_check_box.dart'; - -/// -/// CheckBoxGroup变化监听器 -/// -typedef OnGroupChange = void Function(List checkedIds); - -/// -/// 控制CheckBoxGroup -/// -class TDCheckboxGroupController { - TDCheckboxGroupState? _state; - - /// - /// 选择全部 - /// 这个方法会忽略最大可够选数 - /// - void toggleAll(bool check) { - _state?.toggleAll(check); - } - - /// - /// 反选 - /// - void reverseAll() { - _state?._reverseAll(); - } - - /// - /// 选中某一个选项 - /// - void toggle(String id, bool check) { - _state?.toggle(id, check, true); - } - - /// - /// 打勾的选项 - /// - List allChecked() { - return _state?.checkBoxStates.where((k, v) => v).keys.toList() ?? []; - } - - /// - /// 某一项的选中状态 - /// - bool checked(String id) { - var list = allChecked(); - return list.contains(id); - } -} - -/// -/// CheckBox组,可以通过控制器控制组内的多个CheckBox的选择状态 -/// -/// child的属性可以是任意包含TDCheckBox的容器组件,例如: -/// ```dart -/// TDCheckboxGroup( -/// child: Row( -/// children: [ -/// TDCheckBox(), -/// Column( -/// children: [ -/// TDCheckBox() -/// ... -/// ] -/// ) -/// ... -/// ] -/// ) -/// ) -/// ``` -/// -/// -class TDCheckboxGroup extends StatefulWidget { - /// - /// 可以是任意包含TDCheckBox的容器,比如: - /// ``` - /// Row( - /// children: [ - /// TDCheckBox(), - /// TDCheckBox(), - /// ... - /// ] - /// ) - /// ``` - /// - final Widget child; - - /// - /// 状态变化监听器 - /// - final OnGroupChange? onChangeGroup; - - /// - /// 可以通过控制器操作勾选状态 - /// - final TDCheckboxGroupController? controller; - - /// - /// 最多可以勾选多少 - /// - final int? maxChecked; - - /// - /// 勾选的CheckBox id列表 - /// - final List? checkedIds; - - /// - /// 超过最大可勾选的个数 - /// - final VoidCallback? onOverloadChecked; - - /// CheckBox标题的行数 - final int? titleMaxLine; - - - /// CheckBox完全自定义内容 - final ContentBuilder? customContentBuilder; - - /// CheckBoxicon和文字的距离 - final double? spacing; - - /// CheckBox复选框样式:圆形或方形 - final TDCheckboxStyle? style; - - /// - /// 文字相对icon的方位 - /// - final TDContentDirection? contentDirection; - - /// 自定义选择icon的样式 - final IconBuilder? customIconBuilder; - - const TDCheckboxGroup( - {required this.child, - Key? key, - this.onChangeGroup, - this.controller, - this.checkedIds, - this.maxChecked, - this.titleMaxLine, - this.customContentBuilder, - this.contentDirection, - this.style, - this.spacing, - this.customIconBuilder, - this.onOverloadChecked}) : super(key: key); - - @override - State createState() { - return TDCheckboxGroupState(); - } -} - - -class TDCheckboxGroupState extends State { - /// - /// 管理所有子CheckBox的状态 - /// - Map checkBoxStates = {}; - - @override - void initState() { - super.initState(); - // 如果有controller的话,把state设置给controller - widget.controller?._state = this; - - _syncCheckState(widget.checkedIds); - } - - /// 把group中配置的默认选中id,同步到状态中 - void _syncCheckState(List? checkIds) { - checkBoxStates.clear(); - checkIds?.forEach((element) { - checkBoxStates[element] = true; - }); - } - - - @override - void didUpdateWidget(TDCheckboxGroup oldWidget) { - super.didUpdateWidget(oldWidget); - final oldCheckIds = oldWidget.checkedIds; - final newCheckIds = widget.checkedIds; - if (oldCheckIds != newCheckIds) { - _syncCheckState(newCheckIds); - } - } - - - /// - /// 根据id获取CheckBox的勾选状态 - /// - /// - bool getCheckBoxStateById(String id, bool checked) { - if (checkBoxStates[id] == null) { - // checkBox本身的状态 - checkBoxStates[id] = checked; - } - return checkBoxStates[id]!; - } - - /// 勾选单个CheckBox - bool toggle(String id, bool check, [bool notify = false]) { - // 检查是否超过用户设置的最大可勾选数 - if (widget.maxChecked != null && check) { - if (checkBoxStates.count((k, v) => v) >= widget.maxChecked!) { - widget.onOverloadChecked?.call(); - return false; - } - } - checkBoxStates[id] = check; - if (notify) { - setState(() {}); - } - _notifyChange(); - return true; - } - - /// 操作所有CheckBox - void toggleAll(bool check, [bool notify = true]) { - var isChanged = false; - checkBoxStates.forEachCanBreak((k, v) { - if (check) { - if (!toggle(k, check)) { - // 勾选失败,退出循环 - return true; - } - } else { - toggle(k, check); - } - isChanged = true; - return false; - }); - - if (isChanged && notify) { - setState(() {}); - _notifyChange(); - } - } - - /// 反选 - void _reverseAll() { - final reverseValue = - checkBoxStates.map((key, value) => MapEntry(key, !value)); - checkBoxStates.forEach((key, value) { - checkBoxStates[key] = false; - }); - checkBoxStates.forEach((k, v) { - var check = reverseValue[k] ?? false; - toggle(k, check); - }); - setState(() {}); - _notifyChange(); - } - - void _notifyChange() { - final change = widget.onChangeGroup; - if (change != null) { - final checkedIds = checkBoxStates.where((k, v) => v).keys.toList(); - change.call(checkedIds); - } - } - - @override - Widget build(BuildContext context) { - return TDCheckboxGroupInherited(this, widget.child); - } -} - -class TDCheckboxGroupInherited extends InheritedWidget { - final TDCheckboxGroupState state; - - /// - /// 获取树上的Group节点 - /// - static TDCheckboxGroupInherited? of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType(); - } - - const TDCheckboxGroupInherited(this.state, Widget child, {Key? key}) : super(child: child, key: key); - - @override - bool updateShouldNotify(covariant TDCheckboxGroupInherited oldWidget) { - var notify = - oldWidget.state.checkBoxStates.keys != state.checkBoxStates.keys; - return true; - } -} diff --git a/lib/src/components/dialog/td_alert_dialog.dart b/lib/src/components/dialog/td_alert_dialog.dart deleted file mode 100644 index 9cff0331b..000000000 --- a/lib/src/components/dialog/td_alert_dialog.dart +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Created by haozhicao@tencent.com on 6/20/22. - * td_alert_dialog.dart - * - */ - -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; -import '../../util/auto_size.dart'; -import 'td_dialog_widget.dart'; - -/// 弹窗控件 -class TDAlertDialog extends StatelessWidget { - const TDAlertDialog({ - Key? key, - this.backgroundColor = Colors.white, - this.radius = 8.0, - this.title, - this.titleColor = Colors.black, - this.content, - this.contentColor, - this.contentMaxHeight = 0, - this.leftBtn, - this.rightBtn, - }) : assert((title != null || content != null)), - _vertical = false, - _buttons = null, - super(key: key); - - /// 背景颜色 - final Color backgroundColor; - - /// 圆角 - final double radius; - - /// 标题 - final String? title; - - /// 标题颜色 - final Color titleColor; - - /// 内容 - final String? content; - - /// 内容颜色 - final Color? contentColor; - - /// 内容的最大高度,默认为0,也就是不限制高度 - final double contentMaxHeight; - - // 选项是否是垂直排布,默认是左右排布 - final bool _vertical; - // 垂直排布的按钮列表 - final List? _buttons; - - final TDDialogButton? leftBtn; - final TDDialogButton? rightBtn; - - const TDAlertDialog.vertical({ - Key? key, - required List buttons, - this.backgroundColor = Colors.white, - this.radius = 8.0, - this.title, - this.titleColor = Colors.black, - this.content, - this.contentColor, - this.contentMaxHeight = 0, - }) : _vertical = true, - leftBtn = null, - rightBtn = null, - _buttons = buttons, - super(key: key); - - @override - Widget build(BuildContext context) { - // 标题和内容不能同时为空 - return Center( - child: Container( - width: 320.scale, - decoration: BoxDecoration( - color: backgroundColor, // 底色 - borderRadius: BorderRadius.all(Radius.circular(radius)), - ), - child: Column(mainAxisSize: MainAxisSize.min, children: [ - TDInfoWidget( - title: title, - titleColor: titleColor, - content: content, - contentColor: contentColor, - contentMaxHeight: contentMaxHeight, - ), - const TDDivider( - height: 1, - ), - _vertical - ? _verticalButtons(context) - : _horizontalButtons(context), - ]))); - } - - Widget _horizontalButtons(BuildContext context) { - final left = leftBtn ?? TDDialogButton(title: '取消', action: () {}); - final right = - rightBtn ?? TDDialogButton(title: '好的', action: () {}); - return HorizontalButtons( - leftBtn: left, - rightBtn: right, - ); - } - - Widget _verticalButtons(BuildContext context) { - var widgets = []; - _buttons!.asMap().forEach((index, value) { - Widget btn = TDDialogTextButton( - buttonText: value.title, - buttonTextColor: value.titleColor, - onPressed: (){ - Navigator.pop(context); - value.action(); - }, - ); - widgets.add(btn); - if (index != _buttons!.length - 1) { - widgets.add( - const TDDivider( - height: 1, - ), - ); - } - }); - return Column( - children: widgets, - ); - } -} - -/// 弹窗按钮 -class TDDialogButton { - Color? titleColor; - FontWeight? fontWeight; - final String title; - final Function() action; - - TDDialogButton({ - required this.title, - required this.action, - this.titleColor, - this.fontWeight, - }); -} diff --git a/lib/src/components/dialog/td_confirm_dialog.dart b/lib/src/components/dialog/td_confirm_dialog.dart deleted file mode 100644 index 3cbad9dd9..000000000 --- a/lib/src/components/dialog/td_confirm_dialog.dart +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Created by haozhicao@tencent.com on 6/20/22. - * td_confirm_dialog.dart - * - */ - -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; -import '../../util/auto_size.dart'; -import 'td_dialog_widget.dart'; - -/// 弹窗控件 -class TDConfirmDialog extends StatelessWidget { - const TDConfirmDialog({ - Key? key, - this.action, - this.backgroundColor = Colors.white, - this.radius = 8.0, - this.title, - this.titleColor = Colors.black, - this.content, - this.contentColor, - this.contentMaxHeight = 0, - this.buttonText = '知道了', - this.buttonTextColor, - }) : super(key: key); - - /// 标题 - final String? title; - - /// 标题颜色 - final Color titleColor; - - /// 内容 - final String? content; - - /// 内容颜色 - final Color? contentColor; - - /// 内容的最大高度,默认为0,也就是不限制高度 - final double contentMaxHeight; - - /// 按钮文字 - final String? buttonText; - - /// 按钮文字颜色 - final Color? buttonTextColor; - - /// 点击 - final Function()? action; - - /// 背景颜色 - final Color backgroundColor; - - /// 圆角 - final double radius; - - @override - Widget build(BuildContext context) { - // 标题和内容不能同时为空 - assert((title != null || content != null)); - return Center( - child: Container( - width: 320.scale, - decoration: BoxDecoration( - color: backgroundColor, // 底色 - borderRadius: BorderRadius.all(Radius.circular(radius)), - ), - child: Column(mainAxisSize: MainAxisSize.min, children: [ - TDInfoWidget( - title: title, - titleColor: titleColor, - content: content, - contentColor: contentColor, - contentMaxHeight: contentMaxHeight, - ), - const TDDivider( - height: 1, - ), - TDDialogTextButton( - buttonText: buttonText, - buttonTextColor: buttonTextColor, - onPressed: () { - Navigator.pop(context); - if (action != null) { - action!(); - } - }, - ), - ]))); - } -} diff --git a/lib/src/components/dialog/td_dialog.dart b/lib/src/components/dialog/td_dialog.dart deleted file mode 100644 index da63806d5..000000000 --- a/lib/src/components/dialog/td_dialog.dart +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Created by haozhicao@tencent.com on 6/17/22. - * td_dialog.dart - * - */ - - -export 'td_alert_dialog.dart'; -export 'td_confirm_dialog.dart'; -export 'td_image_dialog.dart'; -export 'td_input_dialog.dart'; - diff --git a/lib/src/components/dialog/td_dialog_widget.dart b/lib/src/components/dialog/td_dialog_widget.dart deleted file mode 100644 index f47dc12a6..000000000 --- a/lib/src/components/dialog/td_dialog_widget.dart +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Created by haozhicao@tencent.com on 6/20/22. - * td_dialog_widget.dart - * - */ - -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; - -/// 弹窗标题 -class TDDialogTitle extends StatelessWidget { - const TDDialogTitle({ - Key? key, - this.title = '对话框标题', - this.titleColor = Colors.black, - }) : super(key: key); - - /// 标题颜色 - final Color titleColor; - - /// 标题文字 - final String title; - - @override - Widget build(BuildContext context) { - // 标题和内容不能同时为空 - return TDText( - title, - textColor: titleColor, - fontWeight: FontWeight.w600, - font: Font(size: 16, lineHeight: 24), - textAlign: TextAlign.center, - ); - } -} - -/// 弹窗内容 -class TDDialogContent extends StatelessWidget { - const TDDialogContent({ - Key? key, - this.content = '当前弹窗内容', - this.contentColor = const Color(0x66000000), - }) : super(key: key); - - /// 标题颜色 - final Color contentColor; - - /// 标题文字 - final String content; - - @override - Widget build(BuildContext context) { - // 标题和内容不能同时为空 - return TDText( - content, - textColor: contentColor, - font: Font(size: 16, lineHeight: 24), - textAlign: TextAlign.center, - ); - } -} - -/// 弹窗信息 -class TDInfoWidget extends StatelessWidget { - const TDInfoWidget({ - Key? key, - this.title, - this.titleColor = Colors.black, - this.content, - this.contentColor, - this.contentMaxHeight = 0, - }) : super(key: key); - - /// 标题 - final String? title; - - /// 标题颜色 - final Color titleColor; - - /// 内容 - final String? content; - - /// 内容颜色 - final Color? contentColor; - - /// 内容的最大高度,默认为0,也就是不限制高度 - final double contentMaxHeight; - - @override - Widget build(BuildContext context) { - // 标题和内容不能同时为空 - assert((title != null || content != null)); - return Container( - padding: const EdgeInsets.fromLTRB(24, 32, 24, 32), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - title != null - ? TDText( - title, - textColor: titleColor, - fontWeight: FontWeight.w600, - font: Font(size: 16, lineHeight: 24), - textAlign: TextAlign.center, - ) - : Container(), - content == null - ? Container() - : Container( - padding: EdgeInsets.fromLTRB( - 0, (title != null && content != null) ? 8.0 : 0, 0, 0), - constraints: contentMaxHeight > 0 - ? BoxConstraints( - maxHeight: contentMaxHeight, - ) - : null, - child: Scrollbar( - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: TDDialogContent( - content: content!, - ), - ), - ), - ), - ], - )); - } -} - -/// 水平按钮 -class HorizontalButtons extends StatelessWidget { - const HorizontalButtons({ - Key? key, - required this.leftBtn, - required this.rightBtn, - }) : super(key: key); - - /// 标题颜色 - final TDDialogButton leftBtn; - - /// 标题文字 - final TDDialogButton rightBtn; - - @override - Widget build(BuildContext context) { - // 标题和内容不能同时为空 - return Row( - children: [ - Expanded( - child: TDDialogTextButton( - buttonText: leftBtn.title, - buttonTextColor: leftBtn.titleColor ?? const Color(0xe6000000), - onPressed: () { - Navigator.pop(context); - leftBtn.action(); - }, - ), - ), - const TDDivider( - height: 57, - width: 1, - direction: Axis.vertical, - ), - Expanded( - child: TDDialogTextButton( - buttonText: rightBtn.title, - buttonTextColor: - rightBtn.titleColor ?? TDTheme.of(context).brandColor8, - onPressed: () { - Navigator.pop(context); - rightBtn.action(); - }, - ), - ), - ], - ); - } -} - -/// 弹窗标题 -class TDDialogTextButton extends StatelessWidget { - const TDDialogTextButton({ - Key? key, - this.buttonText = '按钮', - this.buttonTextColor, - required this.onPressed, - this.height = 56.0, - this.width, - }) : super(key: key); - - /// 按钮文字 - final String? buttonText; - - /// 按钮文字颜色 - final Color? buttonTextColor; - - /// 按钮宽度 - final double? width; - - /// 按钮高度 - final double? height; - - /// 点击 - final Function() onPressed; - - @override - Widget build(BuildContext context) { - return TextButton( - onPressed: onPressed, - style: TextButton.styleFrom( - primary: buttonTextColor ?? TDTheme.of(context).brandColor8, - padding: const EdgeInsets.all(0), - ), - child: SizedBox( - height: height, - width: width, - child: Center( - child: TDText( - buttonText, - textColor: buttonTextColor ?? TDTheme.of(context).brandColor8, - fontWeight: FontWeight.w600, - ), - ), - ), - ); - } -} diff --git a/lib/src/components/dialog/td_image_dialog.dart b/lib/src/components/dialog/td_image_dialog.dart deleted file mode 100644 index 073ab8447..000000000 --- a/lib/src/components/dialog/td_image_dialog.dart +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Created by haozhicao@tencent.com on 6/20/22. - * td_image_dialog.dart - * - */ - -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; -import '../../util/auto_size.dart'; -import 'td_dialog_widget.dart'; - -/// 弹窗控件 -class TDImageDialog extends StatelessWidget { - const TDImageDialog({ - Key? key, - required this.image, - this.backgroundColor = Colors.white, - this.radius = 8.0, - this.title = '对话框标题', - this.titleColor = Colors.black, - this.content, - this.contentColor, - this.leftBtn, - this.rightBtn, - }) : assert((title != null || content != null)), - super(key: key); - - /// 背景颜色 - final Color backgroundColor; - - /// 圆角 - final double radius; - - /// 标题 - final String? title; - - /// 标题颜色 - final Color titleColor; - - /// 内容 - final String? content; - - /// 内容颜色 - final Color? contentColor; - - final TDDialogButton? leftBtn; - final TDDialogButton? rightBtn; - - final Image image; - - @override - Widget build(BuildContext context) { - return Center( - child: Container( - width: 320.scale, - decoration: BoxDecoration( - color: backgroundColor, // 底色 - borderRadius: BorderRadius.all(Radius.circular(radius)), - ), - child: Column(mainAxisSize: MainAxisSize.min, children: [ - ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(radius), - topRight: Radius.circular(radius)), - child: SizedBox( - width: 320.scale, - height: 140.scale, - child: FittedBox( - fit: BoxFit.cover, - child: image, - ), - ), - ), - TDInfoWidget( - title: title, - titleColor: titleColor, - content: content, - contentColor: contentColor, - ), - const TDDivider( - height: 1, - ), - _horizontalButtons(context), - ])), - ); - } - - Widget _horizontalButtons(BuildContext context) { - final left = leftBtn ?? TDDialogButton(title: '取消', action: () {}); - final right = - rightBtn ?? TDDialogButton(title: '好的', action: () {}); - return HorizontalButtons( - leftBtn: left, - rightBtn: right, - ); - } -} diff --git a/lib/src/components/dialog/td_input_dialog.dart b/lib/src/components/dialog/td_input_dialog.dart deleted file mode 100644 index 587e20433..000000000 --- a/lib/src/components/dialog/td_input_dialog.dart +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Created by haozhicao@tencent.com on 6/20/22. - * td_input_dialog.dart - * - */ - -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; -import '../../util/auto_size.dart'; -import 'td_dialog_widget.dart'; - -/// 弹窗控件 -class TDInputDialog extends StatelessWidget { - const TDInputDialog({ - Key? key, - required this.textEditingController, - this.backgroundColor = Colors.white, - this.radius = 8.0, - this.title = '输入框标题', - this.titleColor = Colors.black, - this.content, - this.contentColor, - this.leftBtn, - this.rightBtn, - }) : assert((title != null || content != null)), - super(key: key); - - /// 背景颜色 - final Color backgroundColor; - - /// 圆角 - final double radius; - - /// 标题 - final String? title; - - /// 标题颜色 - final Color titleColor; - - /// 内容 - final String? content; - - /// 内容颜色 - final Color? contentColor; - - /// 输入controller - final TextEditingController textEditingController; - - final TDDialogButton? leftBtn; - final TDDialogButton? rightBtn; - - @override - Widget build(BuildContext context) { - return AlertDialog( - contentPadding: EdgeInsets.zero, - content: Container( - width: 320.scale, - decoration: BoxDecoration( - color: backgroundColor, // 底色 - borderRadius: BorderRadius.all(Radius.circular(radius)), - ), - child: Column(mainAxisSize: MainAxisSize.min, children: [ - TDInfoWidget( - title: title, - titleColor: titleColor, - content: content, - contentColor: contentColor, - ), - Container( - color: Colors.white, - height: 80, - padding: const EdgeInsets.fromLTRB(24, 0, 24, 24), - child: TextField( - controller: textEditingController, - autofocus: true, - decoration: InputDecoration( - contentPadding: const EdgeInsets.fromLTRB(10, 0, 10, 0), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(3), - borderSide: BorderSide.none), - hintText: '请输入', - fillColor: const Color(0xfff0f0f0), - filled: true, - // labelText: '左上角', - ), - ), - ), - const TDDivider( - height: 1, - ), - _horizontalButtons(context), - ])), - ); - } - - Widget _horizontalButtons(BuildContext context) { - final left = leftBtn ?? TDDialogButton(title: '取消', action: () {}); - final right = - rightBtn ?? TDDialogButton(title: '好的', action: () {}); - return HorizontalButtons( - leftBtn: left, - rightBtn: right, - ); - } -} diff --git a/lib/src/components/divider/td_divider.dart b/lib/src/components/divider/td_divider.dart deleted file mode 100644 index 6a946889d..000000000 --- a/lib/src/components/divider/td_divider.dart +++ /dev/null @@ -1,139 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; -import 'dashed_widget.dart'; - -/// 分割线 -/// 对于非flutter原有的控件,则只需满足TDesign规范即可; -/// 如果有业务在实际使用,还需兼容实际业务场景。 -class TDDivider extends StatelessWidget { - const TDDivider({ - Key? key, - this.color, - this.margin, - this.width, - this.height, - this.text, - this.widget, - this.gapPadding, - this.hideLine = false, - this.isDashed = false, - this.direction = Axis.horizontal, - }) : super(key: key); - - /// 线条颜色 - final Color? color; - - /// 外部填充 - final EdgeInsetsGeometry? margin; - - /// 线条和中间文本之间的填充 - final EdgeInsetsGeometry? gapPadding; - - /// 宽度,需要竖向线条时使用 - final double? width; - - /// 高度,横向线条使用 - final double? height; - - /// 文本字符串,使用默认样式 - final String? text; - - /// 中间控件,可自定义样式 - final Widget? widget; - - /// 隐藏线条,使用纯文本分割 - final bool hideLine; - - /// 是否为虚线 - final bool isDashed; - - /// 方向,竖直虚线必须传 - final Axis direction; - - @override - Widget build(BuildContext context) { - // 普通直线 - if (widget == null && text == null) { - return _buildLine(context, - width: width, height: height, margin: margin, color: color); - } - - // 隐藏线条,纯文本分割 - if (hideLine) { - return Container( - width: width, - height: height, - margin: margin, - child: _buildMiddleWidget(context), - ); - } - - // 文本+线条 - return Container( - width: width, - margin: margin, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Center( - child: _buildLine( - context, - height: height ?? 0.5, - color: color ?? TDTheme.of(context).grayColor3, - ), - )), - Padding( - padding: gapPadding ?? const EdgeInsets.only(left: 12, right: 12), - child: _buildMiddleWidget(context)), - Expanded( - child: Center( - child: _buildLine( - context, - height: height ?? 0.5, - color: color ?? TDTheme.of(context).grayColor3, - ))), - ], - ), - ); - } - - /// 绘制线条 - Container _buildLine(BuildContext context, - {double? width, - double? height, - EdgeInsetsGeometry? margin, - Color? color}) { - if (isDashed) { - return Container( - width: width, - margin: margin, - child: DashedWidget( - width: width, - height: height, - color: color ?? TDTheme.of(context).grayColor3, - direction: direction, - ), - ); - } else { - return Container( - width: width, - height: height ?? 0.5, - margin: margin, - color: color ?? TDTheme.of(context).grayColor3, - ); - } - } - - /// 构建中间控件 - Widget _buildMiddleWidget(BuildContext context) { - return widget ?? - TDText( - text, - font: TDTheme.of(context).fontXS, - textColor: TDTheme.of(context).fontGyColor3, - forceVerticalCenter: true, - ); - } -} diff --git a/lib/src/components/empty/td_empty.dart b/lib/src/components/empty/td_empty.dart deleted file mode 100644 index 87f9c5cbf..000000000 --- a/lib/src/components/empty/td_empty.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; - -typedef TDTapEvent = void Function(); - -enum TDEmptyType { plain, operation } - -class TDEmpty extends StatelessWidget { - const TDEmpty( - {this.type = TDEmptyType.plain, - this.image, - this.emptyText, - this.operationText, - this.onTapEvent, - Key? key}) - : super(key: key); - - /// 点击事件 - final TDTapEvent? onTapEvent; - final Widget? image; - final String? emptyText; - final String? operationText; - final TDEmptyType type; - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - image ?? Container(), - const Padding(padding: EdgeInsets.only(top: 30)), - TDText( - emptyText ?? '', - fontWeight: FontWeight.w400, - font: TDTheme.of(context).fontS, - textColor: TDTheme.of(context).fontGyColor3, - ), - (type == TDEmptyType.operation) - ? Padding( - padding: const EdgeInsets.only(top: 24), - child: TDButton( - content: operationText ?? '', - size: TDButtonSize.small, - width: 160, - onTap: () { - if (onTapEvent != null) { - onTapEvent!(); - } - }, - )) - : Container() - ], - ), - ); - } -} diff --git a/lib/src/components/icon/td_icons.dart b/lib/src/components/icon/td_icons.dart deleted file mode 100644 index 8ab1e6bc4..000000000 --- a/lib/src/components/icon/td_icons.dart +++ /dev/null @@ -1,513 +0,0 @@ -import 'package:flutter/widgets.dart'; - -// GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: non_constant_identifier_names -// ignore_for_file: constant_identifier_names -@immutable -class _TDIconsData extends IconData { - const _TDIconsData(int codePoint, this.name) - : super( - codePoint, - fontFamily: 'TDIcons', - fontPackage: 'tdesign_flutter', - ); - - final String name; -} - -@immutable -class TDIcons { - const TDIcons._(); - - static const add_circle = _TDIconsData(0xf101, 'add_circle'); - static const add_rectangle = _TDIconsData(0xf102, 'add_rectangle'); - static const add = _TDIconsData(0xf103, 'add'); - static const app = _TDIconsData(0xf104, 'app'); - static const arrow_down_rectangle = - _TDIconsData(0xf105, 'arrow_down_rectangle'); - static const arrow_down = _TDIconsData(0xf106, 'arrow_down'); - static const arrow_left = _TDIconsData(0xf107, 'arrow_left'); - static const arrow_right = _TDIconsData(0xf108, 'arrow_right'); - static const arrow_up = _TDIconsData(0xf109, 'arrow_up'); - static const attach = _TDIconsData(0xf10a, 'attach'); - static const backtop_rectangle = _TDIconsData(0xf10b, 'backtop_rectangle'); - static const backtop = _TDIconsData(0xf10c, 'backtop'); - static const backward = _TDIconsData(0xf10d, 'backward'); - static const barcode = _TDIconsData(0xf10e, 'barcode'); - static const books = _TDIconsData(0xf10f, 'books'); - static const browse_off = _TDIconsData(0xf110, 'browse_off'); - static const browse = _TDIconsData(0xf111, 'browse'); - static const bulletpoint = _TDIconsData(0xf112, 'bulletpoint'); - static const calendar = _TDIconsData(0xf113, 'calendar'); - static const call = _TDIconsData(0xf114, 'call'); - static const caret_down_small = _TDIconsData(0xf115, 'caret_down_small'); - static const caret_down = _TDIconsData(0xf116, 'caret_down'); - static const caret_left_small = _TDIconsData(0xf117, 'caret_left_small'); - static const caret_left = _TDIconsData(0xf118, 'caret_left'); - static const caret_right_small = _TDIconsData(0xf119, 'caret_right_small'); - static const caret_right = _TDIconsData(0xf11a, 'caret_right'); - static const caret_up_small = _TDIconsData(0xf11b, 'caret_up_small'); - static const caret_up = _TDIconsData(0xf11c, 'caret_up'); - static const cart = _TDIconsData(0xf11d, 'cart'); - static const chart_bar = _TDIconsData(0xf11e, 'chart_bar'); - static const chart_bubble = _TDIconsData(0xf11f, 'chart_bubble'); - static const chart_pie = _TDIconsData(0xf120, 'chart_pie'); - static const chart = _TDIconsData(0xf121, 'chart'); - static const chat = _TDIconsData(0xf122, 'chat'); - static const check_circle_filled = - _TDIconsData(0xf123, 'check_circle_filled'); - static const check_circle = _TDIconsData(0xf124, 'check_circle'); - static const check_rectangle_filled = - _TDIconsData(0xf125, 'check_rectangle_filled'); - static const check_rectangle = _TDIconsData(0xf126, 'check_rectangle'); - static const check = _TDIconsData(0xf127, 'check'); - static const chevron_down_circle = - _TDIconsData(0xf128, 'chevron_down_circle'); - static const chevron_down_rectangle = - _TDIconsData(0xf129, 'chevron_down_rectangle'); - static const chevron_down = _TDIconsData(0xf12a, 'chevron_down'); - static const chevron_left_circle = - _TDIconsData(0xf12b, 'chevron_left_circle'); - static const chevron_left_rectangle = - _TDIconsData(0xf12c, 'chevron_left_rectangle'); - static const chevron_left_double = - _TDIconsData(0xf12d, 'chevron_left_double'); - static const chevron_left = _TDIconsData(0xf12e, 'chevron_left'); - static const chevron_right_circle = - _TDIconsData(0xf12f, 'chevron_right_circle'); - static const chevron_right_rectangle = - _TDIconsData(0xf130, 'chevron_right_rectangle'); - static const chevron_right_double = - _TDIconsData(0xf131, 'chevron_right_double'); - static const chevron_right = _TDIconsData(0xf132, 'chevron_right'); - static const chevron_up_circle = _TDIconsData(0xf133, 'chevron_up_circle'); - static const chevron_up_rectangle = - _TDIconsData(0xf134, 'chevron_up_rectangle'); - static const chevron_up = _TDIconsData(0xf135, 'chevron_up'); - static const circle = _TDIconsData(0xf136, 'circle'); - static const clear = _TDIconsData(0xf137, 'clear'); - static const close_circle_filled = - _TDIconsData(0xf138, 'close_circle_filled'); - static const close_circle = _TDIconsData(0xf139, 'close_circle'); - static const close_rectangle = _TDIconsData(0xf13a, 'close_rectangle'); - static const close = _TDIconsData(0xf13b, 'close'); - static const cloud_download = _TDIconsData(0xf13c, 'cloud_download'); - static const cloud_upload = _TDIconsData(0xf13d, 'cloud_upload'); - static const cloud = _TDIconsData(0xf13e, 'cloud'); - static const code = _TDIconsData(0xf13f, 'code'); - static const control_platform = _TDIconsData(0xf140, 'control_platform'); - static const creditcard = _TDIconsData(0xf141, 'creditcard'); - static const dashboard = _TDIconsData(0xf142, 'dashboard'); - static const delete = _TDIconsData(0xf143, 'delete'); - static const desktop = _TDIconsData(0xf144, 'desktop'); - static const discount_filled = _TDIconsData(0xf145, 'discount_filled'); - static const discount = _TDIconsData(0xf146, 'discount'); - static const download = _TDIconsData(0xf147, 'download'); - static const edit_1 = _TDIconsData(0xf148, 'edit_1'); - static const edit = _TDIconsData(0xf149, 'edit'); - static const ellipsis = _TDIconsData(0xf14a, 'ellipsis'); - static const enter = _TDIconsData(0xf14b, 'enter'); - static const error_circle_filled = - _TDIconsData(0xf14c, 'error_circle_filled'); - static const error_circle = _TDIconsData(0xf14d, 'error_circle'); - static const error = _TDIconsData(0xf14e, 'error'); - static const file_add = _TDIconsData(0xf14f, 'file_add'); - static const file_copy = _TDIconsData(0xf150, 'file_copy'); - static const file_excel = _TDIconsData(0xf151, 'file_excel'); - static const file_icon = _TDIconsData(0xf152, 'file_icon'); - static const file_image = _TDIconsData(0xf153, 'file_image'); - static const file_paste = _TDIconsData(0xf154, 'file_paste'); - static const file_pdf = _TDIconsData(0xf155, 'file_pdf'); - static const file_powerpoint = _TDIconsData(0xf156, 'file_powerpoint'); - static const file_unknown = _TDIconsData(0xf157, 'file_unknown'); - static const file_word = _TDIconsData(0xf158, 'file_word'); - static const file = _TDIconsData(0xf159, 'file'); - static const filter_clear = _TDIconsData(0xf15a, 'filter_clear'); - static const filter = _TDIconsData(0xf15b, 'filter'); - static const flag = _TDIconsData(0xf15c, 'flag'); - static const folder_add = _TDIconsData(0xf15d, 'folder_add'); - static const folder_open = _TDIconsData(0xf15e, 'folder_open'); - static const folder = _TDIconsData(0xf15f, 'folder'); - static const fork = _TDIconsData(0xf160, 'fork'); - static const format_horizontal_align_bottom = - _TDIconsData(0xf161, 'format_horizontal_align_bottom'); - static const format_horizontal_align_center = - _TDIconsData(0xf162, 'format_horizontal_align_center'); - static const format_horizontal_align_top = - _TDIconsData(0xf163, 'format_horizontal_align_top'); - static const format_vertical_align_center = - _TDIconsData(0xf164, 'format_vertical_align_center'); - static const format_vertical_align_left = - _TDIconsData(0xf165, 'format_vertical_align_left'); - static const format_vertical_align_right = - _TDIconsData(0xf166, 'format_vertical_align_right'); - static const forward = _TDIconsData(0xf167, 'forward'); - static const fullscreen_exit = _TDIconsData(0xf168, 'fullscreen_exit'); - static const fullsreen = _TDIconsData(0xf169, 'fullsreen'); - static const gender_female = _TDIconsData(0xf16a, 'gender_female'); - static const gender_male = _TDIconsData(0xf16b, 'gender_male'); - static const gift = _TDIconsData(0xf16c, 'gift'); - static const heart_filled = _TDIconsData(0xf16d, 'heart_filled'); - static const heart = _TDIconsData(0xf16e, 'heart'); - static const help_circle_filled = _TDIconsData(0xf16f, 'help_circle_filled'); - static const help_circle = _TDIconsData(0xf170, 'help_circle'); - static const help = _TDIconsData(0xf171, 'help'); - static const history = _TDIconsData(0xf172, 'history'); - static const home = _TDIconsData(0xf173, 'home'); - static const hourglass = _TDIconsData(0xf174, 'hourglass'); - static const image = _TDIconsData(0xf175, 'image'); - static const info_circle_filled_1 = - _TDIconsData(0xf176, 'info_circle_filled_1'); - static const info_circle_filled = _TDIconsData(0xf177, 'info_circle_filled'); - static const info_circle = _TDIconsData(0xf178, 'info_circle'); - static const internet = _TDIconsData(0xf179, 'internet'); - static const jump = _TDIconsData(0xf17a, 'jump'); - static const laptop = _TDIconsData(0xf17b, 'laptop'); - static const layers = _TDIconsData(0xf17c, 'layers'); - static const link_unlink = _TDIconsData(0xf17d, 'link_unlink'); - static const link = _TDIconsData(0xf17e, 'link'); - static const loading_blue = _TDIconsData(0xf17f, 'loading_blue'); - static const loading = _TDIconsData(0xf180, 'loading'); - static const location = _TDIconsData(0xf181, 'location'); - static const lock_off = _TDIconsData(0xf182, 'lock_off'); - static const lock_on = _TDIconsData(0xf183, 'lock_on'); - static const login = _TDIconsData(0xf184, 'login'); - static const logo_android = _TDIconsData(0xf185, 'logo_android'); - static const logo_apple_filled = _TDIconsData(0xf186, 'logo_apple_filled'); - static const logo_apple = _TDIconsData(0xf187, 'logo_apple'); - static const logo_chrome_filled = _TDIconsData(0xf188, 'logo_chrome_filled'); - static const logo_chrome = _TDIconsData(0xf189, 'logo_chrome'); - static const logo_codepen = _TDIconsData(0xf18a, 'logo_codepen'); - static const logo_github_filled = _TDIconsData(0xf18b, 'logo_github_filled'); - static const logo_github = _TDIconsData(0xf18c, 'logo_github'); - static const logo_ie_filled = _TDIconsData(0xf18d, 'logo_ie_filled'); - static const logo_ie = _TDIconsData(0xf18e, 'logo_ie'); - static const logo_windows_filled = - _TDIconsData(0xf18f, 'logo_windows_filled'); - static const logo_windows = _TDIconsData(0xf190, 'logo_windows'); - static const logout = _TDIconsData(0xf191, 'logout'); - static const mail = _TDIconsData(0xf192, 'mail'); - static const menu_fold = _TDIconsData(0xf193, 'menu_fold'); - static const menu_unfold = _TDIconsData(0xf194, 'menu_unfold'); - static const minus_circle_filled = - _TDIconsData(0xf195, 'minus_circle_filled'); - static const minus_circle = _TDIconsData(0xf196, 'minus_circle'); - static const minus_rectangle = _TDIconsData(0xf197, 'minus_rectangle'); - static const mobile_vibrate = _TDIconsData(0xf198, 'mobile_vibrate'); - static const mobile = _TDIconsData(0xf199, 'mobile'); - static const money_circle = _TDIconsData(0xf19a, 'money_circle'); - static const more = _TDIconsData(0xf19b, 'more'); - static const move = _TDIconsData(0xf19c, 'move'); - static const next = _TDIconsData(0xf19d, 'next'); - static const notification_filled = - _TDIconsData(0xf19e, 'notification_filled'); - static const notification = _TDIconsData(0xf19f, 'notification'); - static const order_adjustment_column = - _TDIconsData(0xf1a0, 'order_adjustment_column'); - static const order_ascending = _TDIconsData(0xf1a1, 'order_ascending'); - static const order_descending = _TDIconsData(0xf1a2, 'order_descending'); - static const page_first = _TDIconsData(0xf1a3, 'page_first'); - static const page_last = _TDIconsData(0xf1a4, 'page_last'); - static const pause_circle_filled = - _TDIconsData(0xf1a5, 'pause_circle_filled'); - static const photo = _TDIconsData(0xf1a6, 'photo'); - static const pin = _TDIconsData(0xf1a7, 'pin'); - static const play_circle_filled = _TDIconsData(0xf1a8, 'play_circle_filled'); - static const play_circle_stroke = _TDIconsData(0xf1a9, 'play_circle_stroke'); - static const play_circle = _TDIconsData(0xf1aa, 'play_circle'); - static const play = _TDIconsData(0xf1ab, 'play'); - static const poweroff = _TDIconsData(0xf1ac, 'poweroff'); - static const precise_monitor = _TDIconsData(0xf1ad, 'precise_monitor'); - static const previous = _TDIconsData(0xf1ae, 'previous'); - static const print = _TDIconsData(0xf1af, 'print'); - static const qrcode = _TDIconsData(0xf1b0, 'qrcode'); - static const queue = _TDIconsData(0xf1b1, 'queue'); - static const rectangle = _TDIconsData(0xf1b2, 'rectangle'); - static const refresh = _TDIconsData(0xf1b3, 'refresh'); - static const remove = _TDIconsData(0xf1b4, 'remove'); - static const rollback = _TDIconsData(0xf1b5, 'rollback'); - static const root_list = _TDIconsData(0xf1b6, 'root_list'); - static const round = _TDIconsData(0xf1b7, 'round'); - static const save = _TDIconsData(0xf1b8, 'save'); - static const scan = _TDIconsData(0xf1b9, 'scan'); - static const search = _TDIconsData(0xf1ba, 'search'); - static const secured = _TDIconsData(0xf1bb, 'secured'); - static const server = _TDIconsData(0xf1bc, 'server'); - static const service = _TDIconsData(0xf1bd, 'service'); - static const setting = _TDIconsData(0xf1be, 'setting'); - static const share = _TDIconsData(0xf1bf, 'share'); - static const shop = _TDIconsData(0xf1c0, 'shop'); - static const slash = _TDIconsData(0xf1c1, 'slash'); - static const sound = _TDIconsData(0xf1c2, 'sound'); - static const star_filled = _TDIconsData(0xf1c3, 'star_filled'); - static const star = _TDIconsData(0xf1c4, 'star'); - static const stop_circle_1 = _TDIconsData(0xf1c5, 'stop_circle_1'); - static const stop_circle_filled = _TDIconsData(0xf1c6, 'stop_circle_filled'); - static const stop_circle = _TDIconsData(0xf1c7, 'stop_circle'); - static const stop = _TDIconsData(0xf1c8, 'stop'); - static const swap_left = _TDIconsData(0xf1c9, 'swap_left'); - static const swap_right = _TDIconsData(0xf1ca, 'swap_right'); - static const swap = _TDIconsData(0xf1cb, 'swap'); - static const thumb_down = _TDIconsData(0xf1cc, 'thumb_down'); - static const thumb_up = _TDIconsData(0xf1cd, 'thumb_up'); - static const time_filled = _TDIconsData(0xf1ce, 'time_filled'); - static const time = _TDIconsData(0xf1cf, 'time'); - static const tips = _TDIconsData(0xf1d0, 'tips'); - static const tools = _TDIconsData(0xf1d1, 'tools'); - static const unfold_less = _TDIconsData(0xf1d2, 'unfold_less'); - static const unfold_more = _TDIconsData(0xf1d3, 'unfold_more'); - static const upload = _TDIconsData(0xf1d4, 'upload'); - static const usb = _TDIconsData(0xf1d5, 'usb'); - static const user_add = _TDIconsData(0xf1d6, 'user_add'); - static const user_avatar = _TDIconsData(0xf1d7, 'user_avatar'); - static const user_circle = _TDIconsData(0xf1d8, 'user_circle'); - static const user_clear = _TDIconsData(0xf1d9, 'user_clear'); - static const user_talk = _TDIconsData(0xf1da, 'user_talk'); - static const user = _TDIconsData(0xf1db, 'user'); - static const user2 = _TDIconsData(0xf1dc, 'user2'); - static const usergroup_add = _TDIconsData(0xf1dd, 'usergroup_add'); - static const usergroup_clear = _TDIconsData(0xf1de, 'usergroup_clear'); - static const usergroup = _TDIconsData(0xf1df, 'usergroup'); - static const video = _TDIconsData(0xf1e0, 'video'); - static const view_column = _TDIconsData(0xf1e1, 'view_column'); - static const view_list = _TDIconsData(0xf1e2, 'view_list'); - static const view_module = _TDIconsData(0xf1e3, 'view_module'); - static const wallet = _TDIconsData(0xf1e4, 'wallet'); - static const wifi = _TDIconsData(0xf1e5, 'wifi'); - static const zoom_in = _TDIconsData(0xf1e6, 'zoom_in'); - static const zoom_out = _TDIconsData(0xf1e7, 'zoom_out'); - - static const all = { - 'add_circle': add_circle, - 'add_rectangle': add_rectangle, - 'add': add, - 'app': app, - 'arrow_down_rectangle': arrow_down_rectangle, - 'arrow_down': arrow_down, - 'arrow_left': arrow_left, - 'arrow_right': arrow_right, - 'arrow_up': arrow_up, - 'attach': attach, - 'backtop_rectangle': backtop_rectangle, - 'backtop': backtop, - 'backward': backward, - 'barcode': barcode, - 'books': books, - 'browse_off': browse_off, - 'browse': browse, - 'bulletpoint': bulletpoint, - 'calendar': calendar, - 'call': call, - 'caret_down_small': caret_down_small, - 'caret_down': caret_down, - 'caret_left_small': caret_left_small, - 'caret_left': caret_left, - 'caret_right_small': caret_right_small, - 'caret_right': caret_right, - 'caret_up_small': caret_up_small, - 'caret_up': caret_up, - 'cart': cart, - 'chart_bar': chart_bar, - 'chart_bubble': chart_bubble, - 'chart_pie': chart_pie, - 'chart': chart, - 'chat': chat, - 'check_circle_filled': check_circle_filled, - 'check_circle': check_circle, - 'check_rectangle_filled': check_rectangle_filled, - 'check_rectangle': check_rectangle, - 'check': check, - 'chevron_down_circle': chevron_down_circle, - 'chevron_down_rectangle': chevron_down_rectangle, - 'chevron_down': chevron_down, - 'chevron_left_circle': chevron_left_circle, - 'chevron_left_rectangle': chevron_left_rectangle, - 'chevron_left_double': chevron_left_double, - 'chevron_left': chevron_left, - 'chevron_right_circle': chevron_right_circle, - 'chevron_right_rectangle': chevron_right_rectangle, - 'chevron_right_double': chevron_right_double, - 'chevron_right': chevron_right, - 'chevron_up_circle': chevron_up_circle, - 'chevron_up_rectangle': chevron_up_rectangle, - 'chevron_up': chevron_up, - 'circle': circle, - 'clear': clear, - 'close_circle_filled': close_circle_filled, - 'close_circle': close_circle, - 'close_rectangle': close_rectangle, - 'close': close, - 'cloud_download': cloud_download, - 'cloud_upload': cloud_upload, - 'cloud': cloud, - 'code': code, - 'control_platform': control_platform, - 'creditcard': creditcard, - 'dashboard': dashboard, - 'delete': delete, - 'desktop': desktop, - 'discount_filled': discount_filled, - 'discount': discount, - 'download': download, - 'edit_1': edit_1, - 'edit': edit, - 'ellipsis': ellipsis, - 'enter': enter, - 'error_circle_filled': error_circle_filled, - 'error_circle': error_circle, - 'error': error, - 'file_add': file_add, - 'file_copy': file_copy, - 'file_excel': file_excel, - 'file_icon': file_icon, - 'file_image': file_image, - 'file_paste': file_paste, - 'file_pdf': file_pdf, - 'file_powerpoint': file_powerpoint, - 'file_unknown': file_unknown, - 'file_word': file_word, - 'file': file, - 'filter_clear': filter_clear, - 'filter': filter, - 'flag': flag, - 'folder_add': folder_add, - 'folder_open': folder_open, - 'folder': folder, - 'fork': fork, - 'format_horizontal_align_bottom': format_horizontal_align_bottom, - 'format_horizontal_align_center': format_horizontal_align_center, - 'format_horizontal_align_top': format_horizontal_align_top, - 'format_vertical_align_center': format_vertical_align_center, - 'format_vertical_align_left': format_vertical_align_left, - 'format_vertical_align_right': format_vertical_align_right, - 'forward': forward, - 'fullscreen_exit': fullscreen_exit, - 'fullsreen': fullsreen, - 'gender_female': gender_female, - 'gender_male': gender_male, - 'gift': gift, - 'heart_filled': heart_filled, - 'heart': heart, - 'help_circle_filled': help_circle_filled, - 'help_circle': help_circle, - 'help': help, - 'history': history, - 'home': home, - 'hourglass': hourglass, - 'image': image, - 'info_circle_filled_1': info_circle_filled_1, - 'info_circle_filled': info_circle_filled, - 'info_circle': info_circle, - 'internet': internet, - 'jump': jump, - 'laptop': laptop, - 'layers': layers, - 'link_unlink': link_unlink, - 'link': link, - 'loading_blue': loading_blue, - 'loading': loading, - 'location': location, - 'lock_off': lock_off, - 'lock_on': lock_on, - 'login': login, - 'logo_android': logo_android, - 'logo_apple_filled': logo_apple_filled, - 'logo_apple': logo_apple, - 'logo_chrome_filled': logo_chrome_filled, - 'logo_chrome': logo_chrome, - 'logo_codepen': logo_codepen, - 'logo_github_filled': logo_github_filled, - 'logo_github': logo_github, - 'logo_ie_filled': logo_ie_filled, - 'logo_ie': logo_ie, - 'logo_windows_filled': logo_windows_filled, - 'logo_windows': logo_windows, - 'logout': logout, - 'mail': mail, - 'menu_fold': menu_fold, - 'menu_unfold': menu_unfold, - 'minus_circle_filled': minus_circle_filled, - 'minus_circle': minus_circle, - 'minus_rectangle': minus_rectangle, - 'mobile_vibrate': mobile_vibrate, - 'mobile': mobile, - 'money_circle': money_circle, - 'more': more, - 'move': move, - 'next': next, - 'notification_filled': notification_filled, - 'notification': notification, - 'order_adjustment_column': order_adjustment_column, - 'order_ascending': order_ascending, - 'order_descending': order_descending, - 'page_first': page_first, - 'page_last': page_last, - 'pause_circle_filled': pause_circle_filled, - 'photo': photo, - 'pin': pin, - 'play_circle_filled': play_circle_filled, - 'play_circle_stroke': play_circle_stroke, - 'play_circle': play_circle, - 'play': play, - 'poweroff': poweroff, - 'precise_monitor': precise_monitor, - 'previous': previous, - 'print': print, - 'qrcode': qrcode, - 'queue': queue, - 'rectangle': rectangle, - 'refresh': refresh, - 'remove': remove, - 'rollback': rollback, - 'root_list': root_list, - 'round': round, - 'save': save, - 'scan': scan, - 'search': search, - 'secured': secured, - 'server': server, - 'service': service, - 'setting': setting, - 'share': share, - 'shop': shop, - 'slash': slash, - 'sound': sound, - 'star_filled': star_filled, - 'star': star, - 'stop_circle_1': stop_circle_1, - 'stop_circle_filled': stop_circle_filled, - 'stop_circle': stop_circle, - 'stop': stop, - 'swap_left': swap_left, - 'swap_right': swap_right, - 'swap': swap, - 'thumb_down': thumb_down, - 'thumb_up': thumb_up, - 'time_filled': time_filled, - 'time': time, - 'tips': tips, - 'tools': tools, - 'unfold_less': unfold_less, - 'unfold_more': unfold_more, - 'upload': upload, - 'usb': usb, - 'user_add': user_add, - 'user_avatar': user_avatar, - 'user_circle': user_circle, - 'user_clear': user_clear, - 'user_talk': user_talk, - 'user': user, - 'user2': user2, - 'usergroup_add': usergroup_add, - 'usergroup_clear': usergroup_clear, - 'usergroup': usergroup, - 'video': video, - 'view_column': view_column, - 'view_list': view_list, - 'view_module': view_module, - 'wallet': wallet, - 'wifi': wifi, - 'zoom_in': zoom_in, - 'zoom_out': zoom_out, - }; -} diff --git a/lib/src/components/image/image_widget.dart b/lib/src/components/image/image_widget.dart deleted file mode 100644 index 38edfe3f3..000000000 --- a/lib/src/components/image/image_widget.dart +++ /dev/null @@ -1,210 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; - -///封装图片加载控件,增加图片加载失败时加载默认图片 -class ImageWidget extends StatefulWidget { - /// 图片地址 - final String src; - - /// 图片宽度 - final double width; - - /// 图片高度 - final double height; - - /// 加载错误时展示Widget - final Widget? errorWidget; - - /// 加载中展示Widget - final Widget? loadingWidget; - - /// 适配样式 - final BoxFit fit; - - /// 以下系统Image属性,释义请参考系统[Image]中注释 - final ImageProvider image; - - final ImageFrameBuilder? frameBuilder; - - final ImageLoadingBuilder? loadingBuilder; - - final ImageErrorWidgetBuilder? errorBuilder; - - final Color? color; - - final Animation? opacity; - - final FilterQuality filterQuality; - - final BlendMode? colorBlendMode; - - final AlignmentGeometry alignment; - - final ImageRepeat repeat; - - final Rect? centerSlice; - - final bool matchTextDirection; - - final bool gaplessPlayback; - - final String? semanticLabel; - - final bool excludeFromSemantics; - - final bool isAntiAlias; - - final int? cacheWidth; - - final int? cacheHeight; - - const ImageWidget({ - Key? key, - required this.image, - this.frameBuilder, - this.loadingBuilder, - this.errorBuilder, - this.semanticLabel, - this.excludeFromSemantics = false, - required this.width, - required this.height, - this.color, - this.opacity, - this.colorBlendMode, - required this.fit, - this.alignment = Alignment.center, - this.repeat = ImageRepeat.noRepeat, - this.centerSlice, - this.matchTextDirection = false, - this.gaplessPlayback = false, - this.isAntiAlias = false, - this.filterQuality = FilterQuality.low, - required this.src, - this.errorWidget, - this.loadingWidget, - this.cacheWidth, - this.cacheHeight - }) : super(key: key); - - ImageWidget.network(this.src, { - Key? key, - required this.width, - required this.height, - double scale = 1.0, - this.errorWidget, - this.fit = BoxFit.none, - this.loadingWidget, - this.frameBuilder, - this.loadingBuilder, - this.errorBuilder, - this.semanticLabel, - this.excludeFromSemantics = false, - this.color, - this.opacity, - this.colorBlendMode, - this.alignment = Alignment.center, - this.repeat = ImageRepeat.noRepeat, - this.centerSlice, - this.matchTextDirection = false, - this.gaplessPlayback = false, - this.filterQuality = FilterQuality.low, - this.isAntiAlias = false, - Map? headers, - this.cacheWidth, this.cacheHeight}) : image = ResizeImage.resizeIfNeeded(cacheWidth, cacheHeight, NetworkImage(src, scale: scale, headers: headers)), - assert(cacheWidth == null || cacheWidth > 0), - assert(cacheHeight == null || cacheHeight > 0), - super(key: key); - - @override - State createState() { - return _StateImageWidget(); - } -} - -class _StateImageWidget extends State { - late Image _image; - bool error = false; - bool loading = true; - - @override - void initState() { - super.initState(); - _image = Image.network( - widget.src, - width: widget.width, - height: widget.height, - fit: widget.fit, - color: widget.color, - frameBuilder: widget.frameBuilder, - loadingBuilder: widget.loadingBuilder, - errorBuilder: widget.errorBuilder, - semanticLabel: widget.semanticLabel, - excludeFromSemantics: widget.excludeFromSemantics, - colorBlendMode: widget.colorBlendMode, - alignment: widget.alignment, - repeat: widget.repeat, - centerSlice: widget.centerSlice, - matchTextDirection: widget.matchTextDirection, - gaplessPlayback: widget.gaplessPlayback, - filterQuality: widget.filterQuality, - isAntiAlias: widget.isAntiAlias, - cacheWidth: widget.cacheWidth, - cacheHeight: widget.cacheHeight, - ); - var resolve = _image.image.resolve(const ImageConfiguration()); - resolve.addListener(ImageStreamListener((_, __) { - - /// 加载成功 - setState(() { - loading = false; - error = false; - }); - }, onChunk: (ImageChunkEvent event) { - /// 加载中 - if (loading == false) { - setState(() { - loading = true; - error = false; - }); - } - }, onError: (dynamic exception, StackTrace? stackTrace) { - /// 加载失败 - if (error == false) { - setState(() { - error = true; - loading = false; - }); - } - })); - } - - @override - Widget build(BuildContext context) { - if (error == false && loading == true) { - return Container( - alignment: widget.alignment, - color: widget.color ?? TDTheme.of(context).grayColor2, - child: widget.loadingWidget ?? Icon( - TDIcons.ellipsis, - size: 22, - color: TDTheme.of(context).fontGyColor3, - ) - ); - } - if (error == true && loading == false) { - return Container( - alignment: widget.alignment, - color: widget.color ?? TDTheme.of(context).grayColor2, - child: widget.errorWidget ?? Icon( - TDIcons.close, - size: 22, - color: TDTheme.of(context).fontGyColor3, - ), - ); - } - if (loading == false && error == false) { - return _image; - } - return Container(); - } -} diff --git a/lib/src/components/image/td_image.dart b/lib/src/components/image/td_image.dart deleted file mode 100644 index d64b8da3c..000000000 --- a/lib/src/components/image/td_image.dart +++ /dev/null @@ -1,360 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; -import '../../util/string_util.dart'; -import 'image_widget.dart'; - -enum TDImageSize { - /// 120*120px - xl, - - /// 72*72 - l, - - /// 56*56 - m, - - /// 48*48 - s, - - /// 32*32 - xs, - - /// 24*24 - xxs -} - -enum TDImageType { - /// 裁剪 - clip, - - /// 适应高 - fitHeight, - - /// 拉伸 - stretch, - - /// 方形, - square, - - /// 圆角方形 - roundedSquare, - - /// 圆形 - circle, -} - -class TDImage extends StatefulWidget { - - const TDImage( - this.imgUrl, { - Key? key, - this.size = TDImageSize.l, - this.type = TDImageType.roundedSquare, - this.errorWidget, - this.loadingWidget, - this.width, - this.height, - this.frameBuilder, - this.loadingBuilder, - this.errorBuilder, - this.semanticLabel, - this.excludeFromSemantics = false, - this.color, - this.opacity, - this.colorBlendMode, - this.alignment = Alignment.center, - this.repeat = ImageRepeat.noRepeat, - this.centerSlice, - this.matchTextDirection = false, - this.gaplessPlayback = false, - this.isAntiAlias = false, - this.filterQuality = FilterQuality.low, - this.cacheHeight, - this.cacheWidth, - }) : super(key: key); - - /// 图片地址 - final String imgUrl; - - /// 图片类型 - final TDImageType type; - - /// 图片大小 - final TDImageSize size; - - /// 加载自定义提示 - final Widget? loadingWidget; - - /// 失败自定义提示 - final Widget? errorWidget; - - /// 自定义宽 - final double? width; - - /// 自定义高 - final double? height; - - /// 以下系统Image属性,释义请参考系统[Image]中注释 - - final ImageFrameBuilder? frameBuilder; - - final ImageLoadingBuilder? loadingBuilder; - - final ImageErrorWidgetBuilder? errorBuilder; - - final Color? color; - - final Animation? opacity; - - final FilterQuality filterQuality; - - final BlendMode? colorBlendMode; - - final AlignmentGeometry alignment; - - final ImageRepeat repeat; - - final Rect? centerSlice; - - final bool matchTextDirection; - - final bool gaplessPlayback; - - final String? semanticLabel; - - final bool excludeFromSemantics; - - final bool isAntiAlias; - - final int? cacheHeight; - - final int? cacheWidth; - - @override - State createState() => _TDImageState(); -} - -class _TDImageState extends State { - double _getImageWidth() { - double width; - switch (widget.size) { - case TDImageSize.xl: - width = 120; - break; - case TDImageSize.l: - width = 72; - break; - case TDImageSize.m: - width = 56; - break; - case TDImageSize.s: - width = 48; - break; - case TDImageSize.xs: - width = 32; - break; - case TDImageSize.xxs: - width = 24; - break; - default: - width = 72; - break; - } - return widget.width ?? width; - } - - double _getImageHeight() { - double height; - height = _getImageWidth(); - return widget.height ?? height; - } - - @override - Widget build(BuildContext context) { - switch (widget.type) { - case TDImageType.clip: - return SizedBox( - width: _getImageWidth(), - height: _getImageHeight(), - child: ImageWidget.network( - widget.imgUrl, - height: _getImageHeight(), - width: _getImageWidth(), - errorWidget: widget.errorWidget, - loadingWidget: widget.loadingWidget, - fit: BoxFit.cover, - color: widget.color, - frameBuilder: widget.frameBuilder, - loadingBuilder: widget.loadingBuilder, - errorBuilder: widget.errorBuilder, - semanticLabel: widget.semanticLabel, - excludeFromSemantics: widget.excludeFromSemantics, - opacity: widget.opacity, - colorBlendMode: widget.colorBlendMode, - alignment: widget.alignment, - repeat: widget.repeat, - centerSlice: widget.centerSlice, - matchTextDirection: widget.matchTextDirection, - gaplessPlayback: widget.gaplessPlayback, - filterQuality: widget.filterQuality, - isAntiAlias: widget.isAntiAlias, - cacheHeight: widget.cacheHeight, - cacheWidth: widget.cacheWidth, - ), - ); - case TDImageType.fitHeight: - return SizedBox( - width: _getImageWidth(), - height: _getImageHeight(), - child: ImageWidget.network( - widget.imgUrl, - height: _getImageHeight(), - width: _getImageWidth(), - errorWidget: widget.errorWidget, - loadingWidget: widget.loadingWidget, - fit: BoxFit.fitHeight, - color: widget.color, - frameBuilder: widget.frameBuilder, - loadingBuilder: widget.loadingBuilder, - errorBuilder: widget.errorBuilder, - semanticLabel: widget.semanticLabel, - excludeFromSemantics: widget.excludeFromSemantics, - opacity: widget.opacity, - colorBlendMode: widget.colorBlendMode, - alignment: widget.alignment, - repeat: widget.repeat, - centerSlice: widget.centerSlice, - matchTextDirection: widget.matchTextDirection, - gaplessPlayback: widget.gaplessPlayback, - filterQuality: widget.filterQuality, - isAntiAlias: widget.isAntiAlias, - cacheHeight: widget.cacheHeight, - cacheWidth: widget.cacheWidth, - ), - ); - case TDImageType.stretch: - return SizedBox( - width: _getImageWidth(), - height: _getImageHeight(), - child: ImageWidget.network( - widget.imgUrl, - height: _getImageHeight(), - width: _getImageWidth(), - errorWidget: widget.errorWidget, - loadingWidget: widget.loadingWidget, - fit: BoxFit.fill, - color: widget.color, - frameBuilder: widget.frameBuilder, - loadingBuilder: widget.loadingBuilder, - errorBuilder: widget.errorBuilder, - semanticLabel: widget.semanticLabel, - excludeFromSemantics: widget.excludeFromSemantics, - opacity: widget.opacity, - colorBlendMode: widget.colorBlendMode, - alignment: widget.alignment, - repeat: widget.repeat, - centerSlice: widget.centerSlice, - matchTextDirection: widget.matchTextDirection, - gaplessPlayback: widget.gaplessPlayback, - filterQuality: widget.filterQuality, - isAntiAlias: widget.isAntiAlias, - cacheHeight: widget.cacheHeight, - cacheWidth: widget.cacheWidth, - ), - ); - case TDImageType.square: - return SizedBox( - width: _getImageWidth(), - height: _getImageHeight(), - child: ImageWidget.network( - widget.imgUrl, - height: _getImageHeight(), - width: _getImageWidth(), - errorWidget: widget.errorWidget, - loadingWidget: widget.loadingWidget, - fit: BoxFit.cover, - color: widget.color, - frameBuilder: widget.frameBuilder, - loadingBuilder: widget.loadingBuilder, - errorBuilder: widget.errorBuilder, - semanticLabel: widget.semanticLabel, - excludeFromSemantics: widget.excludeFromSemantics, - opacity: widget.opacity, - colorBlendMode: widget.colorBlendMode, - alignment: widget.alignment, - repeat: widget.repeat, - centerSlice: widget.centerSlice, - matchTextDirection: widget.matchTextDirection, - gaplessPlayback: widget.gaplessPlayback, - filterQuality: widget.filterQuality, - isAntiAlias: widget.isAntiAlias, - cacheHeight: widget.cacheHeight, - cacheWidth: widget.cacheWidth, - ), - ); - case TDImageType.roundedSquare: - return Container( - width: _getImageWidth(), - height: _getImageHeight(), - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration(borderRadius: BorderRadius.circular(4)), - child: ImageWidget.network( - widget.imgUrl, - height: _getImageHeight(), - width: _getImageWidth(), - errorWidget: widget.errorWidget, - loadingWidget: widget.loadingWidget, - fit: BoxFit.cover, - color: widget.color, - frameBuilder: widget.frameBuilder, - loadingBuilder: widget.loadingBuilder, - errorBuilder: widget.errorBuilder, - semanticLabel: widget.semanticLabel, - excludeFromSemantics: widget.excludeFromSemantics, - opacity: widget.opacity, - colorBlendMode: widget.colorBlendMode, - alignment: widget.alignment, - repeat: widget.repeat, - centerSlice: widget.centerSlice, - matchTextDirection: widget.matchTextDirection, - gaplessPlayback: widget.gaplessPlayback, - filterQuality: widget.filterQuality, - isAntiAlias: widget.isAntiAlias, - cacheHeight: widget.cacheHeight, - cacheWidth: widget.cacheWidth, - )); - case TDImageType.circle: - return Container( - width: _getImageWidth(), - height: _getImageHeight(), - clipBehavior: Clip.hardEdge, - decoration: const BoxDecoration(shape: BoxShape.circle), - child: ImageWidget.network( - widget.imgUrl, - height: _getImageHeight(), - width: _getImageWidth(), - errorWidget: widget.errorWidget, - loadingWidget: widget.loadingWidget, - fit: BoxFit.cover, - color: widget.color, - frameBuilder: widget.frameBuilder, - loadingBuilder: widget.loadingBuilder, - errorBuilder: widget.errorBuilder, - semanticLabel: widget.semanticLabel, - excludeFromSemantics: widget.excludeFromSemantics, - opacity: widget.opacity, - colorBlendMode: widget.colorBlendMode, - alignment: widget.alignment, - repeat: widget.repeat, - centerSlice: widget.centerSlice, - matchTextDirection: widget.matchTextDirection, - gaplessPlayback: widget.gaplessPlayback, - filterQuality: widget.filterQuality, - isAntiAlias: widget.isAntiAlias, - cacheHeight: widget.cacheHeight, - cacheWidth: widget.cacheWidth, - )); - } - } -} diff --git a/lib/src/components/input/td_input.dart b/lib/src/components/input/td_input.dart deleted file mode 100644 index 82e12d274..000000000 --- a/lib/src/components/input/td_input.dart +++ /dev/null @@ -1,511 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import '../../../td_export.dart'; - -enum TDInputType { - normal, - twoLine, - longText, - special -} - -enum TDInputSize { - small, - large -} - -class TDInput extends StatelessWidget { - const TDInput({ - Key? key, - this.width, - this.textStyle, - this.backgroundColor, - this.decoration, - this.leftLabel, - this.readOnly = false, - this.autofocus = false, - this.obscureText = false, - this.onEditingComplete, - this.onSubmitted, - this.hintText, - this.inputType, - this.onChanged, - this.inputFormatters, - this.inputDecoration, - this.maxLines = 1, - this.focusNode, - this.controller, - this.cursorColor, - this.rightBtn, - this.hintTextStyle, - this.onTapBtn, - this.labelWidget, - this.textInputBackgroundColor, - this.contentPadding, - this.type = TDInputType.normal, - this.size = TDInputSize.small, - this.inputWidth = 81, - this.maxNum = 500, - this.errorText = '', - this.textAlign, - this.rightWidget - }) : super(key: key); - - /// 输入框宽度 - final double? width; - - /// 输入框背景色 - final Color? backgroundColor; - - /// 输入框样式 - final Decoration? decoration; - - /// 输入框左侧文案 - final String? leftLabel; - - /// leftLabel右侧组件,支持自定义 - final Widget? labelWidget; - - /// 是否只读 - final bool readOnly; - - /// 提示文案 - final String? hintText; - - /// 键盘类型,数字、字母 - final TextInputType? inputType; - - /// 输入文本变化时回调 - final ValueChanged? onChanged; - - /// 显示输入内容,如限制长度(LengthLimitingTextInputFormatter(6)) - final List? inputFormatters; - - /// controller 用户获取或者赋值输入内容 - final TextEditingController? controller; - - /// 最大输入行数 - final int maxLines; - - /// 获取或者取消焦点使用 - final FocusNode? focusNode; - - /// 是否自动获取焦点 - final bool autofocus; - - /// 是否隐藏输入的文字,一般用在密码输入框中 - final bool obscureText; - - /// 点击键盘完成按钮时触发的回调 - final VoidCallback? onEditingComplete; - - /// 点击键盘完成按钮时触发的回调, 参数值为输入的内容 - final ValueChanged? onSubmitted; - - /// 自定义输入框样式,默认圆角 - final InputDecoration? inputDecoration; - - /// 文本颜色 - final TextStyle? textStyle; - - /// 提示文本颜色,默认为文本颜色 - final TextStyle? hintTextStyle; - - /// 文本框背景色 - final Color? textInputBackgroundColor; - - /// 游标颜色 - final Color? cursorColor; - - /// 右侧按钮 - final Widget? rightBtn; - - /// 右侧按钮点击 - final GestureTapCallback? onTapBtn; - - /// textInput内边距 - final EdgeInsetsGeometry? contentPadding; - - /// 输入框类型 - final TDInputType type; - - /// 输入框规格 - final TDInputSize size; - - /// 输入框宽度 - final double? inputWidth; - - /// 最大字数限制 - final int? maxNum; - - /// 错误提示信息 - final String? errorText; - - /// 文字对齐方向 - final TextAlign? textAlign; - - /// 右侧自定义组件 特殊类型时生效 - final Widget? rightWidget; - - - /// 获取输入框规格 - double getInputPadding() { - switch(size) { - case TDInputSize.small: - return 12; - case TDInputSize.large: - return 16; - } - } - - Widget buildInputView(BuildContext context) { - switch(type) { - case TDInputType.normal: - return buildNormalInput(context); - case TDInputType.twoLine: - return buildTwoLineInput(context); - case TDInputType.special: - return buildSpecialInput(context); - case TDInputType.longText: - return buildLongTextInput(context); - } - } - - Widget buildNormalInput(BuildContext context){ - return Stack( - alignment: Alignment.bottomCenter, - children: [ - Container( - alignment: Alignment.centerLeft, - color: decoration != null ? null : backgroundColor, - decoration: decoration, - child: Row( - crossAxisAlignment: errorText != '' ? CrossAxisAlignment.start : CrossAxisAlignment.center, - children: [ - Visibility( - visible: leftLabel != null, - child: Padding( - padding: EdgeInsets.only(left: 16, top: getInputPadding(), bottom: getInputPadding()), - child: inputWidth != null ? SizedBox( - width: inputWidth, - child: TDText( - leftLabel, - maxLines: 1, - font: TDTheme.of(context).fontM, - fontWeight: FontWeight.w400, - ), - ) : TDText( - leftLabel, - maxLines: 1, - font: TDTheme.of(context).fontM, - fontWeight: FontWeight.w400, - ), - ), - ), - Visibility( - visible: labelWidget != null, - child: labelWidget ?? const SizedBox.shrink(), - ), - Expanded( - flex: 1, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TDInputView( - textStyle: textStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor1), - readOnly: readOnly, - autofocus: autofocus, - obscureText: obscureText, - onEditingComplete: onEditingComplete, - onSubmitted: onSubmitted, - hintText: hintText, - inputType: inputType, - onChanged: onChanged, - inputFormatters: inputFormatters, - inputDecoration: inputDecoration, - maxLines: maxLines, - focusNode: focusNode, - isCollapsed: true, - textAlign: textAlign, - hintTextStyle: hintTextStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor3), - cursorColor: cursorColor, - textInputBackgroundColor: textInputBackgroundColor, - controller: controller, - contentPadding: contentPadding ?? EdgeInsets.only(left: 16, right: 16, bottom: errorText != '' ? 4 : getInputPadding(), top: getInputPadding()), - ), - Visibility(child: Padding( - padding: EdgeInsets.only(left: 16, bottom: getInputPadding()), - child: TDText(errorText, font: TDTheme.of(context).fontXS, textColor: TDTheme.of(context).fontGyColor3,), - ), visible: errorText != '',) - ], - ), - ), - Visibility( - visible: rightBtn != null, - child: GestureDetector( - onTap: onTapBtn, - child: Container( - margin: const EdgeInsets.only(left: 17.5, right: 17.5), - child: rightBtn, - ), - ), - ), - ], - ), - ), - const Visibility(child: TDDivider(margin: EdgeInsets.only(left: 16, ),),), - ], - ); - } - - Widget buildTwoLineInput(BuildContext context){ - return Container( - alignment: Alignment.centerLeft, - color: decoration != null ? null : backgroundColor, - decoration: decoration, - child: Stack( - alignment: Alignment.bottomCenter, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Visibility( - visible: leftLabel != null, - child: Padding( - padding: const EdgeInsets.only(left: 16, top: 12), - child: inputWidth != null ? SizedBox( - width: inputWidth, - child: TDText( - leftLabel, - maxLines: 1, - font: TDTheme.of(context).fontS, - fontWeight: FontWeight.w400, - ), - ) : TDText( - leftLabel, - maxLines: 1, - font: TDTheme.of(context).fontS, - fontWeight: FontWeight.w400, - ), - ), - ), - Container( - padding: const EdgeInsets.only(bottom: 12, top: 7), - alignment: Alignment.center, - child: Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Visibility( - visible: labelWidget != null, - child: labelWidget ?? const SizedBox.shrink(), - ), - Expanded( - flex: 1, - child: TDInputView( - textStyle: textStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor1), - readOnly: readOnly, - autofocus: autofocus, - obscureText: obscureText, - onEditingComplete: onEditingComplete, - onSubmitted: onSubmitted, - hintText: hintText, - inputType: inputType, - onChanged: onChanged, - textAlign: textAlign, - inputFormatters: inputFormatters, - inputDecoration: inputDecoration, - isCollapsed: true, - maxLines: maxLines, - focusNode: focusNode, - hintTextStyle: hintTextStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor3), - cursorColor: cursorColor, - textInputBackgroundColor: textInputBackgroundColor, - controller: controller, - contentPadding: contentPadding ?? const EdgeInsets.only(left: 16, right: 16), - ), - ), - Visibility( - visible: rightBtn != null, - child: GestureDetector( - onTap: onTapBtn, - child: Container( - margin: const EdgeInsets.only(left: 17.5, right: 13.5), - child: rightBtn, - ), - ), - ) - ], - ), - ), - ], - ), - const Visibility(child: TDDivider(margin: EdgeInsets.only(left: 16, ),),), - ], - ), - ); - } - - Widget buildLongTextInput(BuildContext context){ - return Container( - alignment: Alignment.centerLeft, - color: decoration != null ? null : backgroundColor, - decoration: decoration, - height: leftLabel != null ? 197 : 148, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Visibility( - visible: leftLabel != null, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only(left: 16, top: getInputPadding(), bottom: getInputPadding()), - child: TDText( - leftLabel, - maxLines: 2, - fontWeight: FontWeight.w400, - ) - ), - const TDDivider(margin: EdgeInsets.only(left: 16, ),), - ], - ), - ), - Expanded( - flex: 1, - child: TDInputView( - textStyle: textStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor1), - readOnly: readOnly, - autofocus: autofocus, - obscureText: obscureText, - onEditingComplete: onEditingComplete, - onSubmitted: onSubmitted, - hintText: hintText, - inputType: inputType, - textAlign: textAlign, - onChanged: onChanged, - inputFormatters: inputFormatters ?? [LengthLimitingTextInputFormatter(maxNum)], - inputDecoration: inputDecoration, - maxLines: maxLines, - focusNode: focusNode, - hintTextStyle: hintTextStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor3), - cursorColor: cursorColor, - textInputBackgroundColor: textInputBackgroundColor, - controller: controller, - contentPadding: contentPadding ?? const EdgeInsets.only(left: 16, right: 16, top: 12, bottom: 12), - ), - ), - Container( - alignment: Alignment.bottomRight, - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 12), - child: TDText( - '${controller?.text.length}/${maxNum}', - font: TDTheme.of(context).fontXS, - textColor: TDTheme.of(context).fontGyColor3, - ), - ), - ], - ), - ); - } - - Widget buildSpecialInput(BuildContext context){ - return Stack( - alignment: Alignment.bottomCenter, - children: [ - Container( - alignment: Alignment.centerLeft, - color: decoration != null ? null : backgroundColor, - decoration: decoration, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Visibility( - visible: leftLabel != null, - child: Padding( - padding: EdgeInsets.only(left: 16, top: getInputPadding(), bottom: getInputPadding()), - child: inputWidth != null ? SizedBox( - width: inputWidth, - child: TDText( - leftLabel, - maxLines: 1, - font: TDTheme.of(context).fontM, - fontWeight: FontWeight.w400, - ), - ) : TDText( - leftLabel, - maxLines: 1, - font: TDTheme.of(context).fontM, - fontWeight: FontWeight.w400, - ), - ), - ), - Visibility( - visible: labelWidget != null, - child: labelWidget ?? const SizedBox.shrink(), - ), - Row( - children: [ - SizedBox( - width: getTextSize( - hintText ?? '', - textStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor1, fontSize: 16), - ).width + 12, - child: TDInputView( - textStyle: textStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor1), - readOnly: readOnly, - autofocus: autofocus, - obscureText: obscureText, - onEditingComplete: onEditingComplete, - onSubmitted: onSubmitted, - hintText: hintText, - inputType: inputType, - onChanged: onChanged, - inputFormatters: inputFormatters, - inputDecoration: inputDecoration, - maxLines: maxLines, - focusNode: focusNode, - isCollapsed: true, - hintTextStyle: hintTextStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor3), - cursorColor: cursorColor, - textInputBackgroundColor: textInputBackgroundColor, - controller: controller, - textAlign: textAlign, - contentPadding: contentPadding ?? EdgeInsets.only(right: 12, bottom: getInputPadding(), top: getInputPadding()), - ), - ), - Visibility( - visible: rightWidget != null, - child: Container( - margin: EdgeInsets.only(top: getInputPadding(), bottom: getInputPadding(), right: 12), - child: rightWidget, - ), - ), - ], - ), - ], - ), - ), - const Visibility(child: TDDivider(margin: EdgeInsets.only(left: 16, ),),), - ], - ); - } - - Size getTextSize(String text, [TextStyle? style]) { - var painter = TextPainter( - text: TextSpan(text: text, style: style), - textDirection: TextDirection.ltr, - maxLines: 1, - ellipsis: '...', - ); - painter.layout(); - return painter.size; - } - - @override - Widget build(BuildContext context) { - var screenWidth = MediaQuery.of(context).size.width; - return SizedBox(child: buildInputView(context), width: width ?? screenWidth, ); - } -} diff --git a/lib/src/components/loading/td_circle_indicator.dart b/lib/src/components/loading/td_circle_indicator.dart deleted file mode 100644 index c0c6a5ac0..000000000 --- a/lib/src/components/loading/td_circle_indicator.dart +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Created by haozhicao@tencent.com on 6/28/22. - * td_circle_indicator.dart - * - */ - -import 'dart:math'; -import 'dart:ui' as ui; - -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; - -class TDCircleIndicator extends StatefulWidget { - const TDCircleIndicator({ - Key? key, - this.color, - this.size = 20.0, - this.lineWidth = 3.0 - }) : super(key: key); - - final Color? color; - final double size; - final double lineWidth; - - @override - _TDCircleIndicatorState createState() => _TDCircleIndicatorState(); -} - -class _TDCircleIndicatorState extends State - with SingleTickerProviderStateMixin { - late AnimationController _controller; - late Animation _animation1; - - @override - void initState() { - super.initState(); - - _controller = AnimationController( - vsync: this, duration: const Duration(milliseconds: 2000)) - ..addListener(() => setState(() {})) - ..repeat(); - _animation1 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( - parent: _controller, - curve: const Interval(0.0, 1.0, curve: Curves.linear))); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - var value = (_animation1.value) * 2 * pi; - var paintColor = widget.color ?? TDTheme.of(context).brandColor8; - return Transform( - transform: Matrix4.identity()..rotateZ(value), - alignment: FractionalOffset.center, - child: SizedBox.fromSize( - size: Size.square(widget.size), - child: CustomPaint( - painter: _CirclePaint(color: paintColor, width: widget.lineWidth), - ), - ), - ); - } -} - - -class _CirclePaint extends CustomPainter { - final Color color; - final double width; - - _CirclePaint({required this.color, required this.width}); - - final _paint = Paint()..style = PaintingStyle.stroke; - - @override - void paint(Canvas canvas, Size size) { - var minLength = min(size.width, size.height); - _paint.strokeWidth = width; - _paint.shader = ui.Gradient.sweep(Offset(size.width / 2, size.height / 2), - [const Color(0x01ffffff), color]); - if(minLength == size.width){ - canvas.drawArc( - Rect.fromLTWH(0, (size.height - size.width) / 2, size.width, size.width), 0, pi * 2, false, _paint); - } else { - canvas.drawArc( - Rect.fromLTWH((size.width - size.height) / 2, 0, size.height, size.height ), 0, pi * 2, false, _paint); - } - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) { - return true; - } -} diff --git a/lib/src/components/loading/td_loading.dart b/lib/src/components/loading/td_loading.dart deleted file mode 100644 index 534112a67..000000000 --- a/lib/src/components/loading/td_loading.dart +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Created by haozhicao@tencent.com on 6/29/22. - * td_loading.dart - * - */ - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; -import 'td_activity_indicator.dart'; -import 'td_circle_indicator.dart'; -import 'td_point_indicator.dart'; - -/// Loading 尺寸 -enum TDLoadingSize { - small, - medium, - large, -} - -/// Loading的图标 -enum TDLoadingIcon { - circle, - point, - activity, -} - -class TDLoading extends StatelessWidget { - const TDLoading( - {Key? key, - required this.size, - this.icon, - this.iconColor, - this.axis = Axis.vertical, - this.text, - this.customIcon, - this.textColor = Colors.black}) - : super(key: key); - - final TDLoadingSize size; - final TDLoadingIcon? icon; - final Color? iconColor; - final String? text; - final Color textColor; - final Axis axis; - final Widget? customIcon; - - @override - Widget build(BuildContext context) { - return Material( - type: MaterialType.transparency, //透明类型 - child: Center( - //保证控件居中效果 - child: _contentWidget(), - ), - ); - } - - Widget _contentWidget() { - if (icon == null) { - return textWidget(); - } else { - if(customIcon != null){ - return customIcon!; - } - Widget? indicator; - switch (icon!) { - case TDLoadingIcon.activity: - indicator = TDCupertinoActivityIndicator( - activeColor: iconColor, - radius: size == TDLoadingSize.small - ? 10 - : (size == TDLoadingSize.medium ? 11 : 13), - ); - break; - case TDLoadingIcon.circle: - indicator = _getCircleIndicator(); - break; - case TDLoadingIcon.point: - indicator = TDPointBounceIndicator( - color: iconColor, - size: size == TDLoadingSize.small - ? 12 - : (size == TDLoadingSize.medium ? 16 : 20), - ); - break; - default: - indicator = _getCircleIndicator(); - break; - } - - if (text == null) { - return indicator; - } else if (axis == Axis.vertical) { - return Column(mainAxisAlignment: MainAxisAlignment.center, children: [ - indicator, - SizedBox( - height: _getPaddingWidth(), - ), - textWidget(), - ]); - } else { - return Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - indicator, - SizedBox( - width: _getPaddingWidth(), - ), - textWidget() - ]); - } - } - } - - Widget _getCircleIndicator() { - switch(size){ - case TDLoadingSize.large: - return TDCircleIndicator( - color: iconColor, - size: 24, - lineWidth: 3 * 4/3, // 根据small等等比缩放 - ); - case TDLoadingSize.medium: - return TDCircleIndicator( - color: iconColor, - size: 21, - lineWidth: 3 * 7/6, // 根据small等等比缩放 - ); - case TDLoadingSize.small: - return TDCircleIndicator( - color: iconColor, - size: 18, // 设计稿框位24,图形宽位19.5,推导lineWidth为3时,size位18 - lineWidth: 3, - ); - } - } - - double _getPaddingWidth() { - switch(size){ - case TDLoadingSize.large: - return 10; - case TDLoadingSize.medium: - return 8; - case TDLoadingSize.small: - return 6; - } - } - - Font fitFont() { - switch(size){ - case TDLoadingSize.large: - return TDTheme.of().fontM ?? Font(size: 16, lineHeight: 24); - case TDLoadingSize.medium: - return TDTheme.of().fontS ?? Font(size: 14, lineHeight: 22); - case TDLoadingSize.small: - return TDTheme.of().fontXS ?? Font(size: 12, lineHeight: 20); - } - } - - Widget textWidget() { - return TDText( - text, - textColor: textColor, - fontWeight: FontWeight.w400, - font: fitFont(), - textAlign: TextAlign.center, - ); - } -} diff --git a/lib/src/components/navbar/td_nav_bar.dart b/lib/src/components/navbar/td_nav_bar.dart deleted file mode 100644 index 990475a28..000000000 --- a/lib/src/components/navbar/td_nav_bar.dart +++ /dev/null @@ -1,248 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; -import '../../theme/td_spacers.dart'; - -typedef TDBarItemAction = void Function(); - -class TDNavBar extends StatefulWidget implements PreferredSizeWidget { - - const TDNavBar({ - Key? key, - this.leftBarItems, - this.rightBarItems, - this.titleWidget, - this.title, - this.titleColor, - this.titleFont, - this.titleFontFamily, - this.titleFontWeight = FontWeight.w500, - this.centerTitle = true, - this.opacity = 1.0, - this.backgroundColor, - this.titleMargin = 16, - this.padding, - this.height = 44, - this.screenAdaptation = true, - this.useDefaultBack = true, - this.onBack, - this.useBorderStyle = false, - this.border, - }) : super(key: key); - - final List? leftBarItems; - final List? rightBarItems; - final Widget? titleWidget; - final String? title; - final Color? titleColor; - final Font? titleFont; - final FontWeight? titleFontWeight; - final FontFamily? titleFontFamily; - final bool centerTitle; - final double opacity; - final Color? backgroundColor; - final EdgeInsetsGeometry? padding; - - /// 中间文案左右两边间距 - final double titleMargin; - final double height; - - /// 是否进行屏幕适配,默认true - final bool screenAdaptation; - - /// 是否使用默认的返回 - final bool useDefaultBack; - - /// 返回事件 - final VoidCallback? onBack; - - /// 是否使用边框模式 - final bool useBorderStyle; - - /// 边框 - final TDNavBarItemBorder? border; - - @override - State createState() => _TDNavBarState(); - - @override - Size get preferredSize => Size.fromHeight(height); -} - -class _TDNavBarState extends State { - Widget _addBorder(List items) { - var border = widget.border ?? TDNavBarItemBorder(); - var borderColor = border.color ?? TDTheme.of(context).grayColor3; - var children = []; - for (var i = 0; i < items.length; i++) { - children.add(items[i]); - if (widget.useBorderStyle && i != items.length - 1) { - children.add( - Container( - width: border.width, - height: 16.0, - color: borderColor, - ), - ); - } - } - var child = Row( - children: children, - mainAxisSize: MainAxisSize.min, - ); - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(border.radius), - border: Border.all( - color: borderColor, - width: border.width, - ), - ), - padding: border.padding ?? - EdgeInsets.symmetric(horizontal: TDTheme.of(context).spacer4), - child: child, - ); - } - - Widget get backButton { - return TDNavBarItem( - icon: Icons.arrow_back_ios, - action: () { - widget.onBack?.call(); - Navigator.maybePop(context); - }, - ).toWidget(context); - } - - Widget _buildTitleBarItems(bool isLeft) { - var barItems = - (isLeft ? widget.leftBarItems : widget.rightBarItems) ?? []; - var children = barItems - .map( - (e) => e.toWidget(context), - ) - .toList(); - - return Row( - children: [ - if (isLeft && widget.useDefaultBack) - backButton, - if (children.isNotEmpty) - widget.useBorderStyle - ? _addBorder(children) - : Row( - children: children, - mainAxisSize: MainAxisSize.min, - ), - ], - mainAxisSize: MainAxisSize.min, - ); - } - - TextStyle _getTitleStyle(BuildContext context) { - var titleColor = widget.titleColor ?? TDTheme.of(context).fontGyColor1; - - var titleFont = widget.titleFont ?? TDTheme.of(context).fontM; - - return widget.titleFontFamily == null - ? TextStyle( - fontSize: titleFont?.size, - color: titleColor, - fontWeight: widget.titleFontWeight ?? FontWeight.w500, - decoration: TextDecoration.none, - ) - : TextStyle( - fontSize: titleFont?.size, - color: titleColor, - fontWeight: widget.titleFontWeight ?? FontWeight.w500, - decoration: TextDecoration.none, - fontFamily: widget.titleFontFamily!.fontFamily, - package: 'tdesign_flutter'); - } - - Widget _getTitleWidget(BuildContext context) { - return widget.titleWidget ?? - Text( - widget.title ?? '', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: _getTitleStyle(context), - ); - } - - late final top = MediaQuery.of(context).padding.top; - - @override - Widget build(BuildContext context) { - var bcc = widget.backgroundColor ?? TDTheme.of(context).whiteColor1; - if (bcc != Colors.transparent) { - bcc = bcc.withOpacity(widget.opacity); - } - - var paddingTop = widget.screenAdaptation ? top : 0.0; - var padding = widget.padding ?? - EdgeInsets.symmetric( - horizontal: TDTheme.of(context).spacer16, - vertical: TDTheme.of(context).spacer4, - ); - - return Container( - color: bcc, - height: widget.height + paddingTop, - padding: padding.add(EdgeInsets.only(top: paddingTop)), - child: NavigationToolbar( - leading: _buildTitleBarItems(true), - middle: _getTitleWidget(context), - trailing: _buildTitleBarItems(false), - middleSpacing: widget.titleMargin, - centerMiddle: widget.centerTitle, - ), - ); - } -} - -class TDNavBarItem { - IconData? icon; - Color? iconColor; - TDBarItemAction? action; - double? iconSize; - EdgeInsetsGeometry? padding; - Widget? iconWidget; - - TDNavBarItem({ - this.icon, - this.iconColor, - this.action, - this.iconSize = 16.0, - this.padding, - this.iconWidget, - }); - - Widget toWidget(BuildContext context) => GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: action, - child: Padding( - padding: - padding ?? EdgeInsets.all(TDTheme.of(context).spacer8), - child: iconWidget ?? - Icon( - icon, - size: iconSize, - color: iconColor, - ), - ), - ); -} - -class TDNavBarItemBorder { - double width; - double radius; - Color? color; - EdgeInsetsGeometry? padding; - - TDNavBarItemBorder({ - this.width = 1.0, - this.radius = 22.0, - this.color, - this.padding, - }); -} diff --git a/lib/src/components/picker/no_wave_behavior.dart b/lib/src/components/picker/no_wave_behavior.dart deleted file mode 100644 index edb46f938..000000000 --- a/lib/src/components/picker/no_wave_behavior.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import '../../util/platform_util.dart'; - -/// 去掉ListView上下滑动的波纹 -class NoWaveBehavior extends ScrollBehavior { - @override - Widget buildOverscrollIndicator( - BuildContext context, Widget child, ScrollableDetails details) { - if (PlatformUtil.isAndroid || PlatformUtil.isFuchsia) { - return child; - } else { - return super.buildOverscrollIndicator(context, child, details); - } - } -} diff --git a/lib/src/components/picker/td_date_picker.dart b/lib/src/components/picker/td_date_picker.dart deleted file mode 100644 index e5596ad98..000000000 --- a/lib/src/components/picker/td_date_picker.dart +++ /dev/null @@ -1,519 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; -import 'no_wave_behavior.dart'; - -typedef DatePickerCallback = void Function(Map selected); - -/// 时间选择器 -class TDDatePicker extends StatefulWidget { - final String title; // 选择器标题 - final DatePickerCallback? onConfirm; // 选择器确认按钮回调 - final DatePickerCallback? onCancel; // 选择器取消按钮回调 - final Color? backgroundColor; - final Color? titleDividerColor; - final double? topRadius; - final double? titleHeight; - final double? leftPadding; - final double? rightPadding; - - /// 根据距离计算字体颜色、透明度、粗细 - final ItemDistanceCalculator? itemDistanceCalculator; - - /// 选择器List的视窗高度,默认200 - final double pickerHeight; - - /// 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 - final int pickerItemCount; - - /// 自定义选择框样式 - final Widget? customSelectWidget; - - /// 自定义左侧文案样式 - final TextStyle? leftTextStyle; - - /// 自定义右侧文案样式 - final TextStyle? rightTextStyle; - - /// 自定义中间文案样式 - final TextStyle? centerTextStyle; - - /// 适配padding - final EdgeInsets? padding; - - /// 是否展示标题 - final bool showTitle; - - final DatePickerModel model; - - - const TDDatePicker( - {required this.title, - required this.onConfirm, - this.onCancel, - this.backgroundColor, - this.titleDividerColor, - this.topRadius, - this.titleHeight, - this.padding, - this.leftPadding, - this.rightPadding, - this.leftTextStyle, - this.rightTextStyle, - this.centerTextStyle, - this.customSelectWidget, - this.itemDistanceCalculator, - required this.model, - this.showTitle = true, - this.pickerHeight = 200, - required this.pickerItemCount, - Key? key}) - : super(key: key); - - @override - State createState() => _TDDatePickerState(); -} - -class _TDDatePickerState extends State { - - double pickerHeight = 0; - - @override - void initState() { - super.initState(); - pickerHeight = widget.pickerHeight; - } - - @override - Widget build(BuildContext context) { - var maxWidth = MediaQuery.of(context).size.width; - return Container( - width: maxWidth, - padding: widget.padding ?? EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), - decoration: BoxDecoration( - color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(widget.topRadius ?? 0), - topRight: Radius.circular(widget.topRadius ?? 0), - ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Visibility(child: buildTitle(context), visible: widget.showTitle == true,), - Container( - width: maxWidth, - height: 0.5, - color: widget.titleDividerColor ?? TDTheme.of(context).fontGyColor3, - ), - SizedBox( - height: pickerHeight, - child: Stack( - alignment: Alignment.center, - children: [ - widget.customSelectWidget ?? Container( - height: 40, - decoration: BoxDecoration( - border: Border( - top: BorderSide( - color: TDTheme.of(context).fontGyColor3, width: 0.5), - bottom: BorderSide( - color: TDTheme.of(context).fontGyColor3, width: 0.5), - )), - ), - SizedBox( - height: pickerHeight, - width: maxWidth, - child: Row( - children: [ - widget.model.useYear - ? Expanded(child: buildList(context, 0)) - : Container(), - widget.model.useMonth - ? Expanded(child: buildList(context, 1)) - : Container(), - widget.model.useDay - ? Expanded(child: buildList(context, 2)) - : Container(), - widget.model.useHour - ? Expanded(child: buildList(context, 3)) - : Container(), - widget.model.useMinute - ? Expanded(child: buildList(context, 4)) - : Container(), - widget.model.useSecond - ? Expanded(child: buildList(context, 5)) - : Container(), - ], - )) - ], - ), - ) - ], - ), - ); - } - - Widget buildList(context, int whichline) { - /// whichline参数表示这个列表表示的是年,还是月还是日...... - var maxWidth = MediaQuery.of(context).size.width; - return MediaQuery.removePadding( - context: context, - removeTop: true, - child: ScrollConfiguration( - behavior: NoWaveBehavior(), - child: ListWheelScrollView.useDelegate( - itemExtent: pickerHeight / widget.pickerItemCount, - diameterRatio: 100, - controller: widget.model.controllers[whichline], - physics: const FixedExtentScrollPhysics(), - onSelectedItemChanged: (index) { - if (whichline == 0 || whichline == 1) { - // 年月的改变会引起日的改变, 年的改变会引起月的改变 - setState(() { - if (whichline == 0) { - widget.model.refreshMonthDataAndController(); - } - widget.model.refreshDayDataAndController(); - - /// 使用动态高度,强制列表组件的state刷新,以展现更新的数据,详见下方链接 - /// FIX:https://github.com/flutter/flutter/issues/22999 - pickerHeight = - pickerHeight - Random().nextDouble() / 100000000; - }); - } - }, - childDelegate: ListWheelChildBuilderDelegate( - childCount: widget.model.data[whichline].length, - builder: (context, index) { - return Container( - alignment: Alignment.center, - height: pickerHeight / widget.pickerItemCount, - width: maxWidth, - child: TDItemWidget( - index: index, - itemHeight: pickerHeight / widget.pickerItemCount, - content: widget.model.data[whichline][index].toString() + - widget.model.mapping[whichline], - fixedExtentScrollController: - widget.model.controllers[whichline], - itemDistanceCalculator: widget.itemDistanceCalculator, - )); - })), - )); - } - - Widget buildTitle(BuildContext context) { - return Container( - padding: EdgeInsets.only( - left: widget.leftPadding ?? 16, right: widget.rightPadding ?? 16), - - /// 减去分割线的空间 - height: getTitleHeight() - 0.5, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - /// 左边按钮 - GestureDetector( - onTap: () { - if (widget.onCancel != null) { - var selected = { - 'year': widget.model.useYear - ? widget.model.yearFixedExtentScrollController.selectedItem + - widget.model.data[0][0] - : -1, - 'month': widget.model.useMonth - ? widget.model.monthFixedExtentScrollController.selectedItem + - widget.model.data[1][0] - : -1, - 'day': widget.model.useDay - ? widget.model.dayFixedExtentScrollController.selectedItem + - widget.model.data[2][0] - : -1, - 'hour': widget.model.useHour - ? widget.model.hourFixedExtentScrollController.selectedItem - : -1, - 'minute': widget.model.useMinute - ? widget.model.minuteFixedExtentScrollController.selectedItem - : -1, - 'second': widget.model.useSecond - ? widget.model.secondFixedExtentScrollController.selectedItem - : -1, - }; - widget.onCancel!(selected); - } - Navigator.of(context).pop(); - }, - behavior: HitTestBehavior.opaque, - child: TDText( - '取消', - style: widget.leftTextStyle?? TextStyle( - fontSize: TDTheme.of(context).fontM!.size, - color: TDTheme.of(context).fontGyColor2 - ) - )), - - /// 中间title - Expanded( - child: Center( - child: TDText( - widget.title, - style: widget.centerTextStyle ?? TextStyle( - fontSize: TDTheme.of(context).fontM!.size, - fontWeight: FontWeight.w600, - color: TDTheme.of(context).fontGyColor1, - ), - ), - ), - ), - - /// 右边按钮 - GestureDetector( - onTap: () { - if (widget.onConfirm != null) { - var selected = { - 'year': widget.model.useYear - ? widget.model.yearFixedExtentScrollController.selectedItem + - widget.model.data[0][0] - : -1, - 'month': widget.model.useMonth - ? widget.model.monthFixedExtentScrollController.selectedItem + - widget.model.data[1][0] - : -1, - 'day': widget.model.useDay - ? widget.model.dayFixedExtentScrollController.selectedItem + - widget.model.data[2][0] - : -1, - 'hour': widget.model.useHour - ? widget.model.hourFixedExtentScrollController.selectedItem - : -1, - 'minute': widget.model.useMinute - ? widget.model.minuteFixedExtentScrollController.selectedItem - : -1, - 'second': widget.model.useSecond - ? widget.model.secondFixedExtentScrollController.selectedItem - : -1, - }; - widget.onConfirm!(selected); - } - Navigator.of(context).pop(); - }, - behavior: HitTestBehavior.opaque, - child: TDText( - '确认', - style: widget.rightTextStyle?? TextStyle( - fontSize: TDTheme.of(context).fontM!.size, - color: TDTheme.of(context).brandNormalColor - ), - ), - ), - ], - ), - ); - } - - double getTitleHeight() => widget.titleHeight ?? 48; -} - -// 时间选择器的数据类 -class DatePickerModel { - bool useYear; - bool useMonth; - bool useDay; - bool useHour; - bool useMinute; - bool useSecond; - List dateStart; - List dateEnd; - List? dateInitial; - - final mapping = ['年', '月', '日', '时', '分', '秒']; - - late DateTime initialTime; - - /// 这三项随滑动而更新,注意初始化 - late int yearIndex; - late int monthIndex; - late int dayIndex; - - late List> data = [ - List.generate( - dateEnd[0] - dateStart[0] + 1, (index) => index + dateStart[0]), - [], - [], - List.generate(24, (index) => index), - List.generate(60, (index) => index), - List.generate(60, (index) => index), - ]; - late var controllers; - late FixedExtentScrollController yearFixedExtentScrollController; - late FixedExtentScrollController monthFixedExtentScrollController; - late FixedExtentScrollController dayFixedExtentScrollController; - late FixedExtentScrollController hourFixedExtentScrollController; - late FixedExtentScrollController minuteFixedExtentScrollController; - late FixedExtentScrollController secondFixedExtentScrollController; - - DatePickerModel({ - required this.useYear, - required this.useMonth, - required this.useDay, - required this.useHour, - required this.useMinute, - required this.useSecond, - required this.dateStart, - required this.dateEnd, - this.dateInitial - }) { - setInitialTime(); - setInitialMonthData(); - setInitialDayData(); - setControllers(); - addListener(); - } - - void setInitialTime() { - var startTime = DateTime(dateStart[0], dateStart[1], dateStart[2], 0, 0, 0); - var endTime = DateTime(dateEnd[0], dateEnd[1], dateEnd[2], 0, 0, 0); - if (dateInitial != null) { - initialTime = DateTime(dateInitial![0], dateInitial![1], dateInitial![2], 0, 0, 0); - if (initialTime.isBefore(startTime)) { - initialTime = startTime; - } else if (initialTime.isAfter(endTime)) { - initialTime = endTime; - } - return; - } - var now = DateTime.now(); - if (now.year * 500 + now.month * 40 + now.day < - dateStart[0] * 500 + dateStart[1] * 40 + dateStart[2]) { - initialTime = DateTime(dateStart[0], dateStart[1], dateStart[2], now.hour, - now.minute, now.second); - } else if (now.year * 500 + now.month * 40 + now.day > - dateEnd[0] * 500 + dateEnd[1] * 40 + dateEnd[2]) { - initialTime = DateTime( - dateEnd[0], dateEnd[1], dateEnd[2], now.hour, now.minute, now.second); - } else { - initialTime = now; - } - } - - void setInitialMonthData() { - if (dateEnd[0] == dateStart[0]) { - data[1] = List.generate( - dateEnd[1] - dateStart[1] + 1, (index) => index + dateStart[1]); - } else if (initialTime.year == dateStart[0]) { - data[1] = - List.generate(12 - dateStart[1] + 1, (index) => index + dateStart[1]); - } else if (initialTime.year == dateEnd[0]) { - data[1] = List.generate(dateEnd[1], (index) => index + 1); - } else { - data[1] = List.generate(12, (index) => index + 1); - } - } - - void setInitialDayData() { - if (dateEnd[0] == dateStart[0] && dateEnd[1] == dateStart[1]) { - data[2] = List.generate( - dateEnd[2] - dateStart[2] + 1, (index) => index + dateStart[2]); - } else if (initialTime.year == dateStart[0] && - initialTime.month == dateStart[1]) { - data[2] = List.generate( - DateTime(initialTime.year, initialTime.month + 1, 0).day - - dateStart[2] + - 1, - (index) => index + dateStart[2]); - } else if (initialTime.year == dateEnd[0] && - initialTime.month == dateEnd[1]) { - data[2] = List.generate(dateEnd[2], (index) => index + 1); - } else { - data[2] = List.generate( - DateTime(initialTime.year, initialTime.month + 1, 0).day, - (index) => index + 1); - } - } - - void setControllers() { - /// 初始化Index - yearIndex = initialTime.year - data[0][0]; - monthIndex = initialTime.month - data[1][0]; - dayIndex = initialTime.day - data[2][0]; - controllers = [ - FixedExtentScrollController(initialItem: yearIndex), - FixedExtentScrollController(initialItem: monthIndex), - FixedExtentScrollController(initialItem: dayIndex), - FixedExtentScrollController(initialItem: initialTime.hour), - FixedExtentScrollController(initialItem: initialTime.minute), - FixedExtentScrollController(initialItem: initialTime.second) - ]; - yearFixedExtentScrollController = controllers[0]; - monthFixedExtentScrollController = controllers[1]; - dayFixedExtentScrollController = controllers[2]; - hourFixedExtentScrollController = controllers[3]; - minuteFixedExtentScrollController = controllers[4]; - secondFixedExtentScrollController = controllers[5]; - } - - void addListener() { - /// 给年月日加上监控 - yearFixedExtentScrollController.addListener(() { - yearIndex = yearFixedExtentScrollController.selectedItem; - }); - monthFixedExtentScrollController.addListener(() { - monthIndex = monthFixedExtentScrollController.selectedItem; - }); - dayFixedExtentScrollController.addListener(() { - dayIndex = dayFixedExtentScrollController.selectedItem; - }); - } - - void refreshMonthDataAndController() { - var selectedYear = yearIndex + data[0][0]; - if (dateEnd[0] == dateStart[0]) { - data[1] = List.generate( - dateEnd[1] - dateStart[1] + 1, (index) => index + dateStart[1]); - } else if (selectedYear == dateStart[0]) { - data[1] = - List.generate(12 - dateStart[1] + 1, (index) => index + dateStart[1]); - } else if (selectedYear == dateEnd[0]) { - data[1] = List.generate(dateEnd[1], (index) => index + 1); - } else { - data[1] = List.generate(12, (index) => index + 1); - } - monthFixedExtentScrollController.jumpToItem( - monthIndex > data[1].length - 1 ? data[1].length - 1 : monthIndex); - } - - void refreshDayDataAndController() { - /// 在刷新日数据时,年月数据已经是最新的 - var selectedYear = yearIndex + data[0][0]; - var selectedMonth = monthIndex + data[1][0]; - if (dateEnd[0] == dateStart[0] && dateEnd[1] == dateStart[1]) { - data[2] = List.generate( - dateEnd[2] - dateStart[2] + 1, (index) => index + dateStart[2]); - } else if (selectedYear == dateStart[0] && selectedMonth == dateStart[1]) { - data[2] = List.generate( - DateTime(selectedYear, selectedMonth + 1, 0).day - dateStart[2] + 1, - (index) => index + dateStart[2]); - } else if (selectedYear == dateEnd[0] && selectedMonth == dateEnd[1]) { - data[2] = List.generate(dateEnd[2], (index) => index + 1); - } else { - data[2] = List.generate(DateTime(selectedYear, selectedMonth + 1, 0).day, - (index) => index + 1); - } - dayFixedExtentScrollController.jumpToItem( - dayIndex > data[2].length - 1 ? data[2].length - 1 : dayIndex); - } - - Map getSelectedMap() { - var map = { - 'year': yearIndex + data[0][0], - 'month': monthIndex + data[1][0], - 'day' : dayIndex + data[2][0], - }; - return map; - } - -} - diff --git a/lib/src/components/picker/td_item_widget.dart b/lib/src/components/picker/td_item_widget.dart deleted file mode 100644 index e7fb8eed5..000000000 --- a/lib/src/components/picker/td_item_widget.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; - -/// 所有选择器的子项组件 - -class TDItemWidget extends StatefulWidget { - final String content; - final FixedExtentScrollController fixedExtentScrollController; - final int index; - final double itemHeight; - final ItemDistanceCalculator? itemDistanceCalculator; - - - const TDItemWidget( - {required this.fixedExtentScrollController, - required this.index, - required this.content, - required this.itemHeight, - this.itemDistanceCalculator, - Key? key}) - : super(key: key); - - @override - _TDItemWidgetState createState() => _TDItemWidgetState(); -} - -class _TDItemWidgetState extends State { - /// 子项监听滚动,从而刷新自身的颜色 - VoidCallback? listener; - ItemDistanceCalculator? _itemDistanceCalculator; - - @override - void initState() { - super.initState(); - listener = () => setState(() {}); - _itemDistanceCalculator = widget.itemDistanceCalculator; - - /// 子项注册滚动监听 - widget.fixedExtentScrollController.addListener(listener!); - } - - @override - Widget build(BuildContext context) { - /// 子项此时离中心的距离 - /// 不要使用widget.fixedExtentScrollController.selectedItem - /// 其中selectedItem会报错,原因是一开始minScrollExtent为空 - var distance = - (widget.fixedExtentScrollController.offset / widget.itemHeight - - widget.index) - .abs() - .toDouble(); - _itemDistanceCalculator ??= ItemDistanceCalculator(); - return TDText(widget.content, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontWeight: _itemDistanceCalculator!.calculateFontWeight(context, distance), - fontSize: _itemDistanceCalculator!.calculateFont(context, distance), - color: _itemDistanceCalculator!.calculateColor(context, distance) - )); - } - - @override - void dispose() { - /// 在销毁前完成监听注销 - widget.fixedExtentScrollController.removeListener(listener!); - super.dispose(); - } -} - -class ItemDistanceCalculator { - - ItemDistanceCalculator(); - - Color calculateColor(BuildContext context, double distance) { - /// 线性插值 - if (distance < 0.5) { - return TDTheme.of(context).fontGyColor1; - } else { - return TDTheme.of(context).fontGyColor4; - } - } - - FontWeight calculateFontWeight(BuildContext context, double distance) { - return FontWeight.w400; - } - - double calculateFont(BuildContext context, double distance) { - return TDTheme.of(context).fontM!.size; - } -} diff --git a/lib/src/components/picker/td_multi_picker.dart b/lib/src/components/picker/td_multi_picker.dart deleted file mode 100644 index 01add76f5..000000000 --- a/lib/src/components/picker/td_multi_picker.dart +++ /dev/null @@ -1,664 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; - -import 'no_wave_behavior.dart'; - -typedef MultiPickerCallback = void Function(List selected); - -/// 项之间无联动的多项选择器 -class TDMultiPicker extends StatelessWidget { - /// 选择器标题 - final String? title; - - /// 选择器确认按钮回调 - final MultiPickerCallback? onConfirm; - - /// 选择器取消按钮回调 - final MultiPickerCallback? onCancel; - - /// 选择器的数据源 - final List> data; - - /// 选择器List的视窗高度,默认200 - final double pickerHeight; - - /// 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 - final int pickerItemCount; - - /// 自定义选择框样式 - final Widget? customSelectWidget; - - /// 自定义左侧文案样式 - final TextStyle? leftTextStyle; - - /// 自定义右侧文案样式 - final TextStyle? rightTextStyle; - - /// 自定义中间文案样式 - final TextStyle? centerTextStyle; - - final double? titleHeight; - final double? topPadding; - final double? leftPadding; - final double? rightPadding; - final Color? titleDividerColor; - final Color? backgroundColor; - final double? topRadius; - final ItemDistanceCalculator? itemDistanceCalculator; - - /// 适配padding - final EdgeInsets? padding; - - /// 若为null表示全部从零开始 - final List? initialIndexes; - - - - const TDMultiPicker( - {required this.title, - required this.onConfirm, - this.onCancel, - required this.data, - required this.pickerHeight, - required this.pickerItemCount, - this.initialIndexes, - this.leftTextStyle, - this.rightTextStyle, - this.centerTextStyle, - this.titleHeight, - this.topPadding, - this.leftPadding, - this.rightPadding, - this.titleDividerColor, - this.backgroundColor, - this.topRadius, - this.padding, - this.itemDistanceCalculator, - this.customSelectWidget, - Key? key}) - : super(key: key); - - @override - Widget build(BuildContext context) { - var lines = data.length; - var indexes = initialIndexes ?? [for (var i = 0; i < lines; i++) 0]; - var controllers = [ - for (var i = 0; i < lines; i++) - FixedExtentScrollController(initialItem: indexes[i]) - ]; - var maxWidth = MediaQuery.of(context).size.width; - return Container( - width: maxWidth, - padding: padding ?? EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), - decoration: BoxDecoration( - color: backgroundColor ?? TDTheme.of(context).whiteColor1, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(topRadius ?? 0), - topRight: Radius.circular(topRadius ?? 0), - ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - buildTitle(context, controllers), - Container( - width: maxWidth, - height: 0.5, - color: titleDividerColor ?? TDTheme.of(context).fontGyColor3, - ), - Stack( - alignment: Alignment.center, - children: [ - customSelectWidget?? Container( - height: 40, - decoration: BoxDecoration( - border: Border( - top: BorderSide( - color: TDTheme.of(context).fontGyColor3, width: 0.5), - bottom: BorderSide( - color: TDTheme.of(context).fontGyColor3, width: 0.5), - )), - ), - - // 列表 - SizedBox( - height: pickerHeight, - width: maxWidth, - child: Row( - children: [ - for (var i = 0; i < data.length; i++) - Expanded( - child: buildList(context, i, controllers), - ) - ], - )), - ], - ), - ], - ), - ); - } - - Widget buildTitle(BuildContext context, List controllers) { - return Container( - padding: - EdgeInsets.only(left: leftPadding ?? 16, right: rightPadding ?? 16), - - // 减去分割线的空间 - height: getTitleHeight() - 0.5, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - // 左边按钮 - GestureDetector( - onTap: () { - if (onCancel != null) { - onCancel!([ - for (var i = 0; i < controllers.length; i++) - controllers[i].selectedItem - ]); - } - Navigator.of(context).pop(); - }, - behavior: HitTestBehavior.opaque, - child: TDText( - '取消', - style: leftTextStyle?? TextStyle( - fontSize: TDTheme.of(context).fontM!.size, - color: TDTheme.of(context).fontGyColor2 - ), - )), - - // 中间title - Expanded( - child: title == null - ? Container() - : Center( - child: TDText( - title, - style: centerTextStyle ?? TextStyle( - fontSize: TDTheme.of(context).fontM!.size, - fontWeight: FontWeight.w600, - color: TDTheme.of(context).fontGyColor1 - ), - ), - ), - ), - - // 右边按钮 - GestureDetector( - onTap: () { - if (onConfirm != null) { - onConfirm!([ - for (var i = 0; i < controllers.length; i++) - controllers[i].selectedItem - ]); - } - Navigator.of(context).pop(); - }, - behavior: HitTestBehavior.opaque, - child: TDText( - '确定', - style: rightTextStyle?? TextStyle( - fontSize: TDTheme.of(context).fontM!.size, - color: TDTheme.of(context).brandNormalColor - ), - ), - ), - ], - ), - ); - } - - double getTitleHeight() => titleHeight ?? 48; - - Widget buildList(context, int position, List controllers) { - var maxWidth = MediaQuery.of(context).size.width; - return MediaQuery.removePadding( - context: context, - removeTop: true, - child: ScrollConfiguration( - behavior: NoWaveBehavior(), - child: ListWheelScrollView.useDelegate( - itemExtent: pickerHeight / pickerItemCount, - diameterRatio: 100, - controller: controllers[position], - physics: const FixedExtentScrollPhysics(), - childDelegate: ListWheelChildBuilderDelegate( - childCount: data[position].length, - builder: (context, index) { - return Container( - alignment: Alignment.center, - height: pickerHeight / pickerItemCount, - width: maxWidth, - child: TDItemWidget( - index: index, - itemHeight: pickerHeight / pickerItemCount, - content: data[position][index], - itemDistanceCalculator: itemDistanceCalculator, - fixedExtentScrollController: controllers[position], - )); - })), - )); - } -} - -/// 多项联动选择器 -class TDMultiLinkedPicker extends StatefulWidget { - /// 选择器标题 - final String? title; - - /// 选择器确认按钮回调 - final MultiPickerCallback? onConfirm; - - /// 选择器取消按钮回调 - final MultiPickerCallback? onCancel; - - /// 选中数据 - final List selectedData; - - /// 选择器的数据源 - final Map data; - - /// 最大列数 - final int columnNum; - - /// 选择器List的视窗高度 - final double pickerHeight; - - /// 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 - final int pickerItemCount; - - /// 自定义选择框样式 - final Widget? customSelectWidget; - - /// 自定义左侧文案样式 - final TextStyle? leftTextStyle; - - /// 自定义右侧文案样式 - final TextStyle? rightTextStyle; - - /// 自定义中间文案样式 - final TextStyle? centerTextStyle; - - /// 适配padding - final EdgeInsets? padding; - - final double? titleHeight; - final double? topPadding; - final double? leftPadding; - final double? rightPadding; - final Color? titleDividerColor; - final Color? backgroundColor; - final double? topRadius; - final ItemDistanceCalculator? itemDistanceCalculator; - - const TDMultiLinkedPicker({ - this.title, - required this.onConfirm, - this.onCancel, - required this.selectedData, - required this.data, - required this.columnNum, - this.pickerHeight = 200, - this.pickerItemCount = 5, - this.customSelectWidget, - this.leftTextStyle, - this.rightTextStyle, - this.centerTextStyle, - this.titleHeight, - this.topPadding, - this.leftPadding, - this.rightPadding, - this.titleDividerColor, - this.backgroundColor, - this.topRadius, - this.padding, - this.itemDistanceCalculator, - Key? key, - }) : super(key: key); - - @override - State createState() => _TDMultiLinkedPickerState(); -} - -class _TDMultiLinkedPickerState extends State { - late MultiLinkedPickerModel model; - - double pickerHeight = 0; - - @override - void initState() { - super.initState(); - pickerHeight = widget.pickerHeight; - model = MultiLinkedPickerModel( - data: widget.data, columnNum: widget.columnNum, initialData: widget.selectedData); - } - - @override - Widget build(BuildContext context) { - var maxWidth = MediaQuery.of(context).size.width; - return Container( - width: maxWidth, - padding: widget.padding ?? EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), - decoration: BoxDecoration( - color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(widget.topRadius ?? 0), - topRight: Radius.circular(widget.topRadius ?? 0), - ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - buildTitle(context), - Container( - width: maxWidth, - height: 0.5, - color: widget.titleDividerColor ?? TDTheme.of(context).fontGyColor3, - ), - SizedBox( - height: widget.pickerHeight, - child: Stack( - alignment: Alignment.center, - children: [ - widget.customSelectWidget ?? Container( - height: 40, - decoration: BoxDecoration( - border: Border( - top: BorderSide( - color: TDTheme.of(context).fontGyColor3, width: 0.5), - bottom: BorderSide( - color: TDTheme.of(context).fontGyColor3, width: 0.5), - )), - ), - - // 列表 - SizedBox( - height: pickerHeight, - width: maxWidth, - child: Row( - children: [ - for (var i = 0; i < widget.columnNum; i++) - Expanded( - child: buildList(context, i), - ) - ], - )), - ], - ), - ) - ], - ), - ); - } - - Widget buildList(context, int position) { - // position参数表示这个第几列 - var maxWidth = MediaQuery.of(context).size.width; - return MediaQuery.removePadding( - context: context, - removeTop: true, - child: ScrollConfiguration( - behavior: NoWaveBehavior(), - child: ListWheelScrollView.useDelegate( - itemExtent: pickerHeight / widget.pickerItemCount, - diameterRatio: 100, - controller: model.controllers[position], - physics: const FixedExtentScrollPhysics(), - onSelectedItemChanged: (index) { - setState(() { - // 刷新此列右边的所有数据 - model.refreshPresentDataAndController(position, index, false); - - // 使用动态高度,强制列表组件的state刷新,以展现更新的数据,详见下方链接 - // FIX:https://github.com/flutter/flutter/issues/22999 - pickerHeight = - pickerHeight - Random().nextDouble() / 100000000; - }); - }, - childDelegate: ListWheelChildBuilderDelegate( - childCount: model.presentData[position].length, - builder: (context, index) { - return Container( - alignment: Alignment.center, - height: pickerHeight / widget.pickerItemCount, - width: maxWidth, - child: TDItemWidget( - index: index, - itemHeight: pickerHeight / widget.pickerItemCount, - content: - model.presentData[position][index].toString(), - fixedExtentScrollController: - model.controllers[position], - itemDistanceCalculator: widget.itemDistanceCalculator, - )); - })), - )); - } - - Widget buildTitle(BuildContext context) { - return Container( - padding: EdgeInsets.only( - left: widget.leftPadding ?? 16, right: widget.rightPadding ?? 16), - height: getTitleHeight() - 0.5, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - // 左边按钮 - GestureDetector( - onTap: () { - if (widget.onCancel != null) { - widget.onCancel!(model.selectedData); - } - Navigator.of(context).pop(); - }, - behavior: HitTestBehavior.opaque, - child: TDText( - '取消', - style: widget.leftTextStyle ?? TextStyle( - fontSize: TDTheme.of(context).fontM!.size, - color: TDTheme.of(context).fontGyColor2, - ), - )), - - // 中间title - Expanded( - child: widget.title == null - ? Container() - : Center( - child: TDText( - widget.title, - style: widget.centerTextStyle ?? TextStyle( - fontSize: TDTheme.of(context).fontM!.size, - fontWeight: FontWeight.w600, - color: TDTheme.of(context).fontGyColor1 - ), - ), - ), - ), - - // 右边按钮 - GestureDetector( - onTap: () { - if (widget.onConfirm != null) { - widget.onConfirm!(model.selectedData); - } - Navigator.of(context).pop(); - }, - behavior: HitTestBehavior.opaque, - child: TDText( - '确定', - style: widget.rightTextStyle ?? TextStyle( - fontSize: TDTheme.of(context).fontM!.size, - color: TDTheme.of(context).brandNormalColor, - ), - ), - ), - ], - ), - ); - } - - double getTitleHeight() => widget.titleHeight ?? 48; -} - -class MultiLinkedPickerModel { - /// 占位字符 - static const placeData = ''; - - /// 总的数据 - late Map data; - - /// 选中数据下标 - late List selectedIndexes; - - /// 总列数 - late int columnNum; - - /// 选中数据 - late List selectedData; - - late List controllers = []; - - /// 每一列展示的数据 - late List presentData = []; - - MultiLinkedPickerModel({ - required this.data, - required this.columnNum, - required List initialData, - }) { - selectedData = []; - selectedIndexes = []; - for (var i = 0; i < columnNum; ++i) { - if (i >= initialData.length) { - selectedData.add(''); - } else { - selectedData.add(initialData[i]); - } - selectedIndexes.add(0); - } - _init(initialData); - } - - void _init(List initialData) { - int pIndex; - controllers.clear(); - presentData.clear(); - for (var i = 0; i < columnNum; ++i) { - pIndex = 0; - if (i == 0) { - // 第一列 - pIndex = data.keys.toList().indexOf(selectedData[i]); - if (pIndex < 0) { - selectedData[i] = data.keys.first; - pIndex = 0; - } - selectedIndexes[i] = pIndex; - presentData.add(data.keys.toList()); - } else { - // 其他列 - dynamic date = findNextData(i); - if (date is Map) { - pIndex = date.keys.toList().indexOf(selectedData[i]); - if (pIndex < 0) { - selectedData[i] = date.keys.first; - pIndex = 0; - } - presentData.add(date.keys.toList()); - } else if (date is List) { - pIndex = date.indexOf(selectedData[i]); - if (pIndex < 0) { - selectedData[i] = date.first; - pIndex = 0; - } - presentData.add(date); - } else { - selectedData[i] = date; - pIndex = 0; - presentData.add([date]); - } - selectedIndexes[i] = pIndex; - } - controllers.add(FixedExtentScrollController(initialItem: pIndex)); - } - } - /// 对应位置的下一列数据 - dynamic findNextData(int position) { - dynamic nextData; - for (var i = 0; i < position; i++) { - if (i == 0) { - nextData = data[selectedData[0]]; - } else { - dynamic data = nextData[selectedData[i]]; - if (data is Map) { - nextData = data; - } else if (data is List) { - nextData = data; - } else { - nextData = [data]; - } - } - if (!(nextData is Map) && (i < position - 1)) { - return [placeData]; - } - } - return nextData; - } - - /// [position] 变动的列 - /// [selectedIndex] 对应选中的index - /// [jump] 是否需要jumpToItem - void refreshPresentDataAndController(int position, int selectedIndex, bool jump) { - // 新选中的数据 - var selectValue = presentData[position][selectedIndex]; - // 更新选中的数据 - selectedData[position] = selectValue; - selectedIndexes[position] = selectedIndex; - if (jump) { - controllers[position].jumpToItem(selectedIndex); - } - // 如果不是最后一列 数据的变动都会造成剩下列的更新 - if (position < columnNum - 1) { - if (presentData[position].length == 1 && presentData[position].first == placeData) { - presentData[position + 1] = [placeData]; - } else { - presentData[position + 1] = findColumnData(position + 1); - } - refreshPresentDataAndController(position + 1, 0, true); - } - } - - /// 寻找对应位置数据 - List findColumnData(int position) { - dynamic nextData; - for (var i = 0; i < position; i++) { - if (i == 0) { - nextData = data[selectedData[0]]; - } else { - dynamic data = nextData[selectedData[i]]; - if (data is Map) { - nextData = data; - } else if (data is List) { - nextData = data; - } else { - nextData = [data]; - } - } - // 如果是map并且是最后一列 返回对应key - if ((nextData is Map) && (i == position - 1)) { - return nextData.keys.toList(); - } - - // 如果数据还没有到最后就已经不是Map - if (!(nextData is Map) && (i < position - 1)) { - return [placeData]; - } - } - return nextData; - } - -} diff --git a/lib/src/components/picker/td_picker.dart b/lib/src/components/picker/td_picker.dart deleted file mode 100644 index 66da68428..000000000 --- a/lib/src/components/picker/td_picker.dart +++ /dev/null @@ -1,104 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'td_date_picker.dart'; -import 'td_multi_picker.dart'; - -class TDPicker { - static void showDatePicker(context, - {required String title, - required DatePickerCallback? onConfirm, - DatePickerCallback? onCancel, - bool useYear = true, - bool useMonth = true, - bool useDay = true, - bool useHour = false, - bool useMinute = false, - bool useSecond = false, - List dateStart = const [1970, 1, 1], - List? dateEnd, - List? initialDate, - Duration duration = const Duration(milliseconds: 100), - double pickerHeight = 270, - int pickerItemCount = 7}) { - if (dateEnd == null || initialDate == null) { - var now = DateTime.now(); - // 如果未指定结束时间,则取当前时间 - dateEnd ??= [now.year, now.month, now.day]; - initialDate ??= [now.year, now.month, now.day]; - } - showModalBottomSheet( - context: context, - barrierColor: Colors.transparent, - builder: (context) { - return TDDatePicker( - title: title, - onConfirm: onConfirm, - onCancel: onCancel, - model: DatePickerModel( - useYear: useYear, - useMonth: useMonth, - useDay: useDay, - useHour: useHour, - useMinute: useMinute, - useSecond: useSecond, - dateStart: dateStart, - dateEnd: dateEnd!, - dateInitial: initialDate - ), - pickerHeight: pickerHeight, - pickerItemCount: pickerItemCount); - }); - } - - static void showMultiPicker(context, - {String? title, - required MultiPickerCallback? onConfirm, - MultiPickerCallback? onCancel, - required List> data, - List? initialIndexes, - Duration duration = const Duration(milliseconds: 100), - double pickerHeight = 270, - int pickerItemCount = 7}) { - showModalBottomSheet( - context: context, - barrierColor: Colors.transparent, - builder: (context) { - return TDMultiPicker( - title: title, - onConfirm: onConfirm, - onCancel: onCancel, - data: data, - initialIndexes: initialIndexes, - pickerHeight: pickerHeight, - pickerItemCount: pickerItemCount, - ); - }); - } - - static void showMultiLinkedPicker(context, - {String? title, - required MultiPickerCallback? onConfirm, - MultiPickerCallback? onCancel, - required Map data, - required int columnNum, - required List initialData, - Duration duration = const Duration(milliseconds: 100), - double pickerHeight = 248, - int pickerItemCount = 7}) { - showModalBottomSheet( - context: context, - barrierColor: Colors.transparent, - builder: (context) { - return TDMultiLinkedPicker( - title: title, - onConfirm: onConfirm, - onCancel: onCancel, - data: data, - pickerHeight: pickerHeight, - pickerItemCount: pickerItemCount, - columnNum: columnNum, - selectedData: initialData, - ); - }); - } -} diff --git a/lib/src/components/popup/td_popup_route.dart b/lib/src/components/popup/td_popup_route.dart deleted file mode 100644 index 78b94a103..000000000 --- a/lib/src/components/popup/td_popup_route.dart +++ /dev/null @@ -1,115 +0,0 @@ -import 'package:flutter/material.dart'; - -const Duration _bottomSheetEnterDuration = Duration(milliseconds: 250); -const Duration _bottomSheetExitDuration = Duration(milliseconds: 200); - -/// 从屏幕弹出的方向 -enum SlideTransitionFrom { top, right, left, bottom } - -/// -/// 从屏幕的某个方向滑动弹出的Dialog框的路由,比如从顶部、底部、左、右滑出页面 -/// -class TDSlidePopupRoute extends PopupRoute { - TDSlidePopupRoute( - {required this.builder, - this.barrierLabel, - this.modalBarrierColor, - this.isDismissible = true, - this.transitionAnimationController, - this.slideTransitionFrom = SlideTransitionFrom.bottom}); - - final WidgetBuilder builder; - final Color? modalBarrierColor; - final bool isDismissible; - final AnimationController? transitionAnimationController; - - // 设置从屏幕的哪个方向滑出 - final SlideTransitionFrom slideTransitionFrom; - - @override - Duration get transitionDuration => _bottomSheetEnterDuration; - - @override - Duration get reverseTransitionDuration => _bottomSheetExitDuration; - - @override - bool get barrierDismissible => isDismissible; - - @override - final String? barrierLabel; - - @override - Color get barrierColor => modalBarrierColor ?? Colors.black54; - - // 实现转场动画 - @override - Widget buildTransitions(BuildContext context, Animation animation, - Animation secondaryAnimation, Widget child) { - var animValue = decelerateEasing.transform(animation.value); - - return AnimatedBuilder( - animation: animation, - child: child, - builder: (context, child) { - return CustomSingleChildLayout( - delegate: - SlideTransitionLayout(animValue, slideTransitionFrom), - child: child, - ); - }); - } - - @override - Widget buildPage(BuildContext context, Animation animation, - Animation secondaryAnimation) { - return Material( - child: builder.call(context), - ) ; - } -} - -/// 从各个方向弹出的Transition -/// progress为0到1区间的变化值 -/// -class SlideTransitionLayout extends SingleChildLayoutDelegate { - final double progress; - final SlideTransitionFrom slideTransitionFrom; - - SlideTransitionLayout(this.progress, this.slideTransitionFrom); - - @override - BoxConstraints getConstraintsForChild(BoxConstraints constraints) { - return BoxConstraints( - // minWidth: constraints.maxWidth, - maxWidth: constraints.maxWidth, - maxHeight: constraints.maxHeight); - } - - @override - Offset getPositionForChild(Size size, Size childSize) { - var posY = 0.0; - var posX = 0.0; - // 弹出的方向 - switch (slideTransitionFrom) { - case SlideTransitionFrom.top: - posY = -(childSize.height - childSize.height * progress); - break; - case SlideTransitionFrom.left: - posX = -(childSize.width - childSize.width * progress); - break; - case SlideTransitionFrom.right: - posX = size.width - childSize.width * progress; - break; - case SlideTransitionFrom.bottom: - posY = size.height - childSize.height * progress; - break; - } - return Offset(posX, posY); - } - - @override - bool shouldRelayout(SlideTransitionLayout oldDelegate) { - return progress != oldDelegate.progress || - slideTransitionFrom != oldDelegate.slideTransitionFrom; - } -} diff --git a/lib/src/components/radio/td_radio.dart b/lib/src/components/radio/td_radio.dart deleted file mode 100644 index 200663999..000000000 --- a/lib/src/components/radio/td_radio.dart +++ /dev/null @@ -1,167 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; - -enum TDRadioStyle { - circle, // 圆形 - square, // 方形 - check, // 对号样式 -} - -/// -/// 单选框按钮 -/// -class TDRadio extends TDCheckbox { - final TDRadioStyle radioStyle; - - const TDRadio({ - String? id, - Key? key, - String? title, - String? subTitle, - bool enable = true, - int subTitleMaxLine = 1, - int titleMaxLine = 1, - Color? checkedColor, - ContentBuilder? customContentBuilder, - double? spacing, - TDCheckBoxSize size = TDCheckBoxSize.small, - this.radioStyle = TDRadioStyle.circle, - TDContentDirection contentDirection = TDContentDirection.right, - IconBuilder? customIconBuilder, - }) : super( - id: id, - key: key, - title: title, - subTitle: subTitle, - subTitleMaxLine: subTitleMaxLine, - enable: enable, - size: size, - titleMaxLine: titleMaxLine, - customContentBuilder: customContentBuilder, - contentDirection: contentDirection, - spacing: spacing, - customIconBuilder: customIconBuilder, - ); - - @override - Widget buildDefaultIcon( - BuildContext context, TDCheckboxGroupState? groupState, bool isSelected) { - - TDRadioStyle? style ; - if (groupState is TDRadioGroupState) { - style = (groupState.widget as TDRadioGroup).radioStyle; - } - - style = style ?? radioStyle; - - var size = 24.0; - final theme = TDTheme.of(context); - IconData? iconData; - switch (style) { - case TDRadioStyle.check: - iconData = isSelected ? TDIcons.check : null; - break; - case TDRadioStyle.square: - iconData = isSelected - ? TDIcons.check_rectangle_filled - : TDIcons.rectangle; - break; - default: - iconData = isSelected - ? TDIcons.check_circle_filled - : TDIcons.circle; - break; - } - if (iconData != null) { - return Icon( - iconData, - size: size, - color: !enable ? theme.grayColor4 : isSelected ? theme.brandColor8 : theme.grayColor4); - } else { - return SizedBox(width: size, height: size,); - } - - } - - @override - State createState() { - return TDRadioState(); - } -} - -class TDRadioState extends TDCheckboxState { - @override - Widget build(BuildContext context) { - // 检查是否包含在FuiCheckBoxGroup内,如果是的话,状态由Group管理 - final groupState = TDCheckboxGroupInherited.of(context)?.state; - if (groupState is TDRadioGroupState) { - final strictMode = (groupState.widget as TDRadioGroup).strictMode; - // 严格模式下不能取消选项,只能切换 - if (strictMode == true) { - canNotCancel = true; - } - } - return super.build(context); - } -} - -/// -/// RadioGroup分组对象 -/// -/// RadioGroup应该嵌套在RadioGroup内,所有在RadioGroup的RadioButton只能有一个被选中 -/// -/// -/// -class TDRadioGroup extends TDCheckboxGroup { - /// - /// 严格模式下,用户不能取消勾选,只能切换选择项, - /// - final bool strictMode; - final TDRadioStyle? radioStyle; - - TDRadioGroup( - {required Widget child, - Key? key, - String? selectId, // 默认选择项的id - this.strictMode = true, - this.radioStyle, - int? titleMaxLine, // item的行数 - IconBuilder? customIconBuilder, - ContentBuilder? customContentBuilder, - double? spacing, // icon和文字距离 - TDContentDirection? contentDirection, - OnRadioGroupChange? onRadioGroupChange}) // 切换监听 - : super( - child: child, - key: key, - onChangeGroup: (ids) { - onRadioGroupChange?.call(ids.isNotEmpty ? ids[0] : null); - }, - controller: null, - checkedIds: selectId != null ? [selectId] : null, - maxChecked: 1, - titleMaxLine: titleMaxLine, - contentDirection: contentDirection, - customIconBuilder: customIconBuilder, - customContentBuilder: customContentBuilder, - style: null, - spacing: spacing, - ); - - @override - State createState() { - return TDRadioGroupState(); - } -} - -class TDRadioGroupState extends TDCheckboxGroupState { - @override - bool toggle(String id, bool check, [bool notify = false]) { - checkBoxStates.forEach((key, value) { - checkBoxStates[key] = false; - }); - return super.toggle(id, check, true); - } -} - -typedef OnRadioGroupChange = void Function(String? selectedId); diff --git a/lib/src/components/radio/td_radio2.dart b/lib/src/components/radio/td_radio2.dart deleted file mode 100644 index 80b4431b7..000000000 --- a/lib/src/components/radio/td_radio2.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; - -typedef OnCheckValueChanged = void Function(bool selected); - -enum TDRadioType { - circle, - square, - check, -} - -// ignore: must_be_immutable -class TDRadio extends StatefulWidget { - final TDRadioType type; - bool? selected; - final bool? disabled; - final OnCheckValueChanged? onCheckValueChanged; - - TDRadio( - {Key? key, - this.type = TDRadioType.circle, - this.selected = false, - this.disabled = false, - this.onCheckValueChanged}) - : super(key: key); - - @override - State createState() => _TDRadioState(); -} - -class _TDRadioState extends State { - bool selected = false; - - @override - void initState() { - selected = widget.selected ?? false; - super.initState(); - } - - Color _getIconColor(BuildContext context) { - if (widget.disabled == true) { - return TDTheme.of(context).grayColor4; - } - - return selected - ? TDTheme.of(context).brandColor8 - : TDTheme.of(context).grayColor4; - } - - Widget? _getRadioIcon(BuildContext context) { - Widget? icon; - switch (widget.type) { - case TDRadioType.circle: - icon = Icon(selected ? TDIcons.check_circle_filled : TDIcons.circle, - size: 24, color: _getIconColor(context)); - break; - case TDRadioType.square: - icon = Icon( - selected ? TDIcons.check_rectangle_filled : TDIcons.rectangle, - size: 24, - color: _getIconColor(context)); - break; - case TDRadioType.check: - icon = selected - ? Icon(TDIcons.check, size: 24, color: _getIconColor(context)) - : const SizedBox( - width: 0, - height: 0, - ); - break; - } - - return icon; - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - child: Container( - width: 24, - height: 24, - color: Colors.transparent, - child: _getRadioIcon(context), - ), - onTap: () { - if (widget.disabled == true){ - return; - } - - setState(() { - selected = !selected; - widget.selected = selected; - if (widget.onCheckValueChanged != null) { - widget.onCheckValueChanged!(selected); - } - }); - }, - ); - } -} diff --git a/lib/src/components/refresh/td_refresh_header.dart b/lib/src/components/refresh/td_refresh_header.dart deleted file mode 100644 index 7830c572b..000000000 --- a/lib/src/components/refresh/td_refresh_header.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_easyrefresh/easy_refresh.dart'; - -import '../../../td_export.dart'; - -/// TDesign刷新头部 -class TDRefreshHeader extends Header { - - TDRefreshHeader({ - this.key, - double extent = 48.0, - double triggerDistance = 48.0, - bool float = false, - Duration? completeDuration, - bool enableHapticFeedback = true, - bool enableInfiniteRefresh = false, - bool overScroll = true, - this.loadingIcon = TDLoadingIcon.circle, - }) : super( - extent: extent, - triggerDistance: triggerDistance, - float: float, - completeDuration: completeDuration, - enableHapticFeedback: enableHapticFeedback, - enableInfiniteRefresh: enableInfiniteRefresh, - overScroll: overScroll, - ); - - /// Key - final Key? key; - - /// loading样式 - final TDLoadingIcon loadingIcon; - - @override - Widget contentBuilder( - BuildContext context, - RefreshMode refreshState, - double pulledExtent, - double refreshTriggerPullDistance, - double refreshIndicatorExtent, - AxisDirection axisDirection, - bool float, - Duration? completeDuration, - bool enableInfiniteRefresh, - bool success, - bool noMore) { - // 不能为水平方向 - assert( - axisDirection == AxisDirection.down || - axisDirection == AxisDirection.up, - 'Widget cannot be horizontal'); - - return TDLoading( - key: key, - size: TDLoadingSize.medium, - icon: loadingIcon, - iconColor: TDTheme.of(context).brandColor8, - ); - } -} diff --git a/lib/src/components/search/td_search_bar.dart b/lib/src/components/search/td_search_bar.dart deleted file mode 100644 index c22865f41..000000000 --- a/lib/src/components/search/td_search_bar.dart +++ /dev/null @@ -1,289 +0,0 @@ - -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; - -typedef TDSearchBarEvent = void Function(String value); -typedef TDSearchBarCallBack = void Function(); - -class TDSearchBar extends StatefulWidget { - - const TDSearchBar({ - Key? key, - this.placeHolder, - this.onTextChanged, - this.onSubmitted, - this.onEditComplete, - this.autoFocus = false, - this.backgroundColor = Colors.white, - }) : super(key: key); - - final String? placeHolder; - final Color? backgroundColor; - final bool autoFocus; - final TDSearchBarEvent? onTextChanged; - final TDSearchBarEvent? onSubmitted; - final TDSearchBarCallBack? onEditComplete; - - @override - State createState() => _TDSearchBarState(); -} - -enum _TDSearchBarStatus { - unFocus, - animatingToFocus, - focused, - animatingToUnFocus -} - -class _TDSearchBarState extends State with TickerProviderStateMixin { - final FocusNode focusNode = FocusNode(); - final TextEditingController controller = TextEditingController(); - final GlobalKey _textFieldKey = GlobalKey(); - final GlobalKey _phKey = GlobalKey(); - - late AnimationController _animationController; - Animation? _animation; - bool clearBtnHide = true; - bool cancelBtnHide = true; - late _TDSearchBarStatus _status; - @override - void initState() { - super.initState(); - _status = widget.autoFocus ? _TDSearchBarStatus.focused : _TDSearchBarStatus.unFocus; - controller.addListener(() { - var clearVisible = controller.text.isNotEmpty; - _updateClearBtnVisible(clearVisible); - }); - focusNode.addListener(() { - setState(() { - _status = focusNode.hasFocus ? _TDSearchBarStatus.focused : _TDSearchBarStatus.unFocus; - }); - _updateCancelBtnVisible(focusNode.hasFocus); - }); - _animationController = AnimationController(duration: const Duration(milliseconds: 200), vsync: this); - _animationController.addStatusListener((status) { - if(status == AnimationStatus.completed) { - setState(() { - if(_status == _TDSearchBarStatus.animatingToFocus) { - _status = _TDSearchBarStatus.focused; - focusNode.requestFocus(); - } else if(_status == _TDSearchBarStatus.animatingToUnFocus) { - _status = _TDSearchBarStatus.unFocus; - } - }); - } - }); - WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { - var box = _textFieldKey.currentContext?.findRenderObject() as RenderBox?; - var phBox = _phKey.currentContext?.findRenderObject() as RenderBox?; - if (box != null && phBox != null) { - setState(() { - var dx = (box.size.width / 2 + 16.5 - phBox.size.width / 2) / phBox.size.width; - if(dx < 0) { - return; - } - _animation ??= - Tween(begin: Offset.zero, end: Offset(-dx, 0)) - .animate(_animationController); - }); - } - }); - } - - void _updateClearBtnVisible(bool visible) { - setState(() { - clearBtnHide = !visible; - }); - } - - void _updateCancelBtnVisible(bool visible) { - setState(() { - cancelBtnHide = !visible; - }); - } - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.fromLTRB(16, 8, 16, 8), - height: 56, - color: widget.backgroundColor, - child: Stack( - alignment: AlignmentDirectional.center, - children: [ - Row( - children: [ - Expanded( - flex: 1, - child: Container( - height: double.infinity, - decoration: BoxDecoration( - color: TDTheme.of(context).grayColor1, - borderRadius: BorderRadius.circular(4)), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const SizedBox( - width: 12, - ), - Visibility( - visible: _status == _TDSearchBarStatus.focused || controller.text.isNotEmpty, - child: Icon( - TDIcons.search, - size: 24, - color: TDTheme.of(context).fontGyColor3, - ), - ), - const Padding(padding: EdgeInsets.only(left: 3)), - Expanded( - flex: 1, - child: TextField( - key: _textFieldKey, - controller: controller, - autofocus: widget.autoFocus, - cursorColor: TDTheme.of(context).brandColor8, - cursorWidth: 1, - cursorHeight: 18, - focusNode: focusNode, - onChanged: widget.onTextChanged, - onSubmitted: widget.onSubmitted, - style: TextStyle( - fontSize: TDTheme.of(context).fontM?.size, - color: TDTheme.of(context).fontGyColor1), - decoration: InputDecoration( - hintText:(_status != _TDSearchBarStatus.focused) ? '' : widget.placeHolder, - hintStyle: TextStyle( - fontSize: TDTheme.of(context).fontM?.size, - color: TDTheme.of(context).fontGyColor3), - border: InputBorder.none, - isCollapsed: true, - filled: true, - fillColor: TDTheme.of(context).grayColor1, - ), - maxLines: 1, - ), - ), - const Padding(padding: EdgeInsets.only(right: 9)), - Offstage( - offstage: clearBtnHide, - child: GestureDetector( - onTap: () { - controller.clear(); - if (widget.onTextChanged != null) { - widget.onTextChanged!(''); - } - }, - child: Icon( - TDIcons.close_circle_filled, - size: 21, - color: TDTheme.of(context).fontGyColor3, - )), - ), - const Padding(padding: EdgeInsets.only(right: 9)), - ],),),), - Offstage( - offstage: cancelBtnHide, - child: GestureDetector( - onTap: () { - controller.clear(); - if (widget.onTextChanged != null) { - widget.onTextChanged!(''); - } - if(_animation == null) { - focusNode.unfocus(); - setState(() { - _status = _TDSearchBarStatus.unFocus; - }); - return; - } - setState(() { - _status = _TDSearchBarStatus.animatingToUnFocus; - }); - focusNode.unfocus(); - _animationController.reverse(from: _animationController.upperBound); - }, - child: Container( - padding: const EdgeInsets.only(left: 16), - child: Text('取消', - style: TextStyle( - fontSize: TDTheme.of(context).fontM?.size, - color: TDTheme.of(context).brandColor8)), - ),),), - ], - ), - Offstage(offstage: (_status == _TDSearchBarStatus.focused || controller.text.isNotEmpty), child: GestureDetector( - onTap: () { - if(_status == _TDSearchBarStatus.animatingToFocus - || _status == _TDSearchBarStatus.animatingToUnFocus) { - return; - } - if(_animation == null) { - focusNode.requestFocus(); - setState(() { - _status = _TDSearchBarStatus.focused; - }); - return; - } - setState(() { - _status = _TDSearchBarStatus.animatingToFocus; - _animationController.forward(); - }); - }, - child: Stack(children: [ - Container(color: TDTheme.of(context).grayColor1,), - Center(child: _getPlaceHolderWidgets(),) - ],) - ),), - ]), - ); - } - - Widget _getPlaceHolderWidgets() { - return LayoutBuilder(builder: (BuildContext context, BoxConstraints box) { - if(_animation != null) { - return SlideTransition( - position: _animation!, - child: Row( - key: _phKey, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - TDIcons.search, - size: 24, - color: TDTheme.of(context).fontGyColor3, - ), - Padding( - padding: const EdgeInsets.only(left: 3), - child: ConstrainedBox(constraints: BoxConstraints(maxWidth: box.maxWidth - 51,), child: TDText( - widget.placeHolder, - font: TDTheme.of(context).fontM, - textColor: TDTheme.of(context).fontGyColor3, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ),),)],), - ); - } else { - return Row( - key: _phKey, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - TDIcons.search, - size: 24, - color: TDTheme.of(context).fontGyColor3, - ), - Padding( - padding: const EdgeInsets.only(left: 3), - child: ConstrainedBox(constraints: BoxConstraints(maxWidth: box.maxWidth - 51,), child: TDText( - widget.placeHolder, - font: TDTheme.of(context).fontM, - textColor: TDTheme.of(context).fontGyColor3, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ),),)],); - } - }); - } -} diff --git a/lib/src/components/switch/td_switch.dart b/lib/src/components/switch/td_switch.dart deleted file mode 100644 index a0fd58554..000000000 --- a/lib/src/components/switch/td_switch.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -import '../../../td_export.dart'; - -class TDSwitch extends StatefulWidget { - - const TDSwitch({ - Key? key, - this.enable = true, - this.isOn = false, - this.onColor, - this.offColor, - this.onChanged, - }): super(key: key); - - /// 是否可点击 - final bool enable; - - /// 是否打开 - final bool isOn; - - /// 开启颜色 - final Color? onColor; - - /// 关闭颜色 - final Color? offColor; - - ///改变事件 - final ValueChanged? onChanged; - - @override - State createState() { - return TDSwitchState(); - } -} - -class TDSwitchState extends State { - - bool isOn = false; - - @override - void initState() { - super.initState(); - isOn = widget.isOn; - } - - @override - void didUpdateWidget(covariant TDSwitch oldWidget) { - super.didUpdateWidget(oldWidget); - isOn = widget.isOn; - } - - @override - Widget build(BuildContext context) { - final theme = TDTheme.of(context); - Widget current = CupertinoSwitch( - value: isOn, - activeColor: widget.onColor ?? theme.brandColor8, - trackColor: widget.offColor ?? theme.grayColor4, - onChanged: (value) { - widget.onChanged?.call(value); - isOn = value; - setState(() { - - }); - }, - ); - if (!widget.enable) { - current = Opacity( - opacity: 0.4, - child: IgnorePointer( - ignoring: !widget.enable, - child: current, - ), - ); - } - return current; - } -} diff --git a/lib/src/components/tabbar/td_tab.dart b/lib/src/components/tabbar/td_tab.dart deleted file mode 100644 index 5d371e4ba..000000000 --- a/lib/src/components/tabbar/td_tab.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter/material.dart'; - -class TDTab extends Tab { - /// 文字内容 - @override - final String? text; - - /// 子widget - @override - final Widget? child; - - /// 图标 - @override - final Widget? icon; - - /// 图标间距 - @override - final EdgeInsetsGeometry iconMargin; - - /// tab高度 - @override - final double? height; - - @override - const TDTab( - {Key? key, - this.text, - this.child, - this.icon, - this.height, - this.iconMargin = const EdgeInsets.only(bottom: 10.0)}) - : super( - key: key, - text: text, - child: child, - icon: icon, - height: height, - iconMargin: iconMargin); - - @override - Widget build(BuildContext context) { - return Tab( - key: key, - text: text, - child: child, - icon: icon, - iconMargin: iconMargin, - height: height, - ); - } -} diff --git a/lib/src/components/tabbar/td_tab_bar.dart b/lib/src/components/tabbar/td_tab_bar.dart deleted file mode 100644 index 49a16f042..000000000 --- a/lib/src/components/tabbar/td_tab_bar.dart +++ /dev/null @@ -1,324 +0,0 @@ -import 'dart:ui' as ui; - -import 'package:flutter/material.dart'; - -import '../../../td_export.dart'; -import 'td_vertical_tab_bar.dart'; - -bool isCustomStyle = false; - -class TDTabBar extends StatefulWidget { - - const TDTabBar( - {Key? key, - required this.tabs, - this.controller, - this.decoration, - this.backgroundColor, - this.indicatorColor, - this.indicatorWidth, - this.indicatorHeight, - this.labelColor, - this.unselectedLabelColor, - this.isScrollable = false, - this.unselectedLabelStyle, - this.labelStyle, - this.width, - this.height, - this.indicatorPadding, - this.labelPadding, - this.indicator, - this.physics, - this.onTap, - this.isVertical = false, - this.showIndicator = false}) - : assert( - backgroundColor == null || decoration == null, - 'Cannot provide both a backgroundColor and a decoration\n' - 'To provide both, use "decoration: BoxDecoration(color: color)".', - ), - super(key: key); - - /// tab数组 - final List tabs; - - /// tab控制器 - final TabController? controller; - - /// tabBar修饰 - final Decoration? decoration; - - /// tabBar背景色 - final Color? backgroundColor; - - /// tabBar下标颜色 - final Color? indicatorColor; - - /// tabBar下标高度 - final double? indicatorHeight; - - /// tabBar下标宽度 - final double? indicatorWidth; - - /// tabBar 已选标签颜色 - final Color? labelColor; - - /// tabBar未选标签颜色 - final Color? unselectedLabelColor; - - /// 是否滚动 - final bool isScrollable; - - /// 已选label字体 - final TextStyle? labelStyle; - - /// unselectedLabel字体 - final TextStyle? unselectedLabelStyle; - - /// tabBar宽度 - final double? width; - - /// tabBar高度 - final double? height; - - /// 引导padding - final EdgeInsets? indicatorPadding; - - /// 自定义引导控件 - final Decoration? indicator; - - /// 是否展示引导控件 - final bool showIndicator; - - /// 自定义滑动 - final ScrollPhysics? physics; - - /// 点击事件 - final Function(int)? onTap; - - /// tab间距 - final EdgeInsetsGeometry? labelPadding; - - /// 是否是竖向 - final bool isVertical; - - @override - State createState() => _TDTabBarState(); -} - -class _TDTabBarState extends State { - /// 默认高度 - static const double _defaultHeight = 46; - static const double _defaultVerticalHeight = 54; - - @override - Widget build(BuildContext context) { - return Container( - width: widget.width ?? MediaQuery.of(context).size.width, - height: widget.isVertical - ? widget.height ?? _defaultVerticalHeight * widget.tabs.length - : widget.height ?? _defaultHeight, - decoration: - widget.decoration ?? BoxDecoration(color: widget.backgroundColor), - child: widget.isVertical - ? VerticalTabBar( - selectedBackgroundColor: TDTheme.of(context).whiteColor1, - physics: widget.physics, - isScrollable: widget.isScrollable, - indicator: widget.indicator ?? _getIndicator(context), - indicatorColor: - widget.indicatorColor ?? TDTheme.of(context).brandNormalColor, - unselectedLabelColor: widget.unselectedLabelColor ?? - TDTheme.of(context).fontGyColor2, - labelColor: - widget.labelColor ?? TDTheme.of(context).brandNormalColor, - labelStyle: widget.labelStyle ?? _getLabelStyle(context), - labelPadding: widget.labelPadding, - unselectedLabelStyle: widget.unselectedLabelStyle ?? - _getUnSelectLabelStyle(context), - tabs: widget.tabs, - indicatorPadding: widget.indicatorPadding ?? EdgeInsets.zero, - controller: widget.controller, - onTap: (index) { - widget.onTap?.call(index); - }, - ) - : TabBar( - physics: widget.physics, - isScrollable: widget.isScrollable, - indicator: widget.indicator ?? _getIndicator(context), - indicatorColor: - widget.indicatorColor ?? TDTheme.of(context).brandNormalColor, - unselectedLabelColor: widget.unselectedLabelColor ?? - TDTheme.of(context).fontGyColor2, - labelColor: - widget.labelColor ?? TDTheme.of(context).brandNormalColor, - labelStyle: widget.labelStyle ?? _getLabelStyle(context), - labelPadding: widget.labelPadding, - unselectedLabelStyle: widget.unselectedLabelStyle ?? - _getUnSelectLabelStyle(context), - tabs: widget.tabs, - indicatorPadding: widget.indicatorPadding ?? EdgeInsets.zero, - controller: widget.controller, - onTap: (index) { - widget.onTap?.call(index); - }, - ), - ); - } - - TextStyle _getUnSelectLabelStyle(BuildContext context) { - return TextStyle( - fontWeight: FontWeight.w400, - fontSize: TDTheme.of(context).fontXS?.size ?? 14, - color: TDTheme.of(context).fontGyColor2); - } - - TextStyle _getLabelStyle(BuildContext context) { - return TextStyle( - fontWeight: FontWeight.w600, - fontSize: TDTheme.of(context).fontXS?.size ?? 14, - color: TDTheme.of(context).fontGyColor2); - } - - Decoration _getIndicator(BuildContext context) { - return widget.showIndicator - ? widget.isVertical - ? TDTabBarVerticalIndicator( - context: context, - indicatorHeight: widget.indicatorHeight, - indicatorWidth: widget.indicatorWidth) - : TDTabBarIndicator( - context: context, - indicatorHeight: widget.indicatorHeight, - indicatorWidth: widget.indicatorWidth) - : TDNoneIndicator(); - } -} - -/// TDesign自定义下标 -class TDTabBarIndicator extends Decoration { - final BuildContext? context; - final double? indicatorWidth; - final double? indicatorHeight; - - const TDTabBarIndicator( - {this.context, this.indicatorWidth, this.indicatorHeight}); - - @override - BoxPainter createBoxPainter([VoidCallback? onChanged]) => - _TDTabBarIndicatorPainter(this, onChanged!); -} - -class _TDTabBarIndicatorPainter extends BoxPainter { - /// 下标宽度 - static const double _defaultIndicatorWidth = 56; - - /// 下标高度 - static const double _defaultIndicatorHeight = 1.5; - - final TDTabBarIndicator decoration; - - final _paint = Paint(); - - _TDTabBarIndicatorPainter(this.decoration, VoidCallback onChanged) { - if (isCustomStyle) { - _paint.shader = ui.Gradient.linear(Offset.zero, Offset.zero, [ - TDTheme.of(decoration.context).brandNormalColor, - TDTheme.of(decoration.context).brandNormalColor, - ]); - } else { - /// 下标颜色 - _paint.color = TDTheme.of(decoration.context).brandNormalColor; - } - _paint.strokeCap = StrokeCap.round; - } - - @override - void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { - canvas.drawLine( - Offset(offset.dx + (configuration.size!.width - _indicatorWidth()) / 2, - configuration.size!.height - _indicatorHeight()), - Offset(offset.dx + (configuration.size!.width + _indicatorWidth()) / 2, - configuration.size!.height - _indicatorHeight()), - _paint..strokeWidth = _indicatorHeight()); - } - - double _indicatorHeight() => - decoration.indicatorHeight ?? _defaultIndicatorHeight; - - double _indicatorWidth() => - decoration.indicatorWidth ?? _defaultIndicatorWidth; -} - -/// TDesign自定义下标 竖向 -class TDTabBarVerticalIndicator extends Decoration { - final BuildContext? context; - final double? indicatorWidth; - final double? indicatorHeight; - - const TDTabBarVerticalIndicator( - {this.context, this.indicatorWidth, this.indicatorHeight}); - - @override - BoxPainter createBoxPainter([VoidCallback? onChanged]) => - _TDTabBarVerticalIndicatorPainter(this, onChanged!); -} - -class _TDTabBarVerticalIndicatorPainter extends BoxPainter { - /// 下标宽度 - static const double _defaultIndicatorWidth = 1.5; - - /// 下标高度 - static const double _defaultIndicatorHeight = 54; - - final TDTabBarVerticalIndicator decoration; - - final _paint = Paint(); - - _TDTabBarVerticalIndicatorPainter(this.decoration, VoidCallback onChanged) { - if (isCustomStyle) { - _paint.shader = ui.Gradient.linear(Offset.zero, Offset.zero, [ - TDTheme.of(decoration.context).brandNormalColor, - TDTheme.of(decoration.context).brandNormalColor, - ]); - } else { - /// 下标颜色 - _paint.color = TDTheme.of(decoration.context).brandNormalColor; - } - _paint.strokeCap = StrokeCap.round; - } - - @override - void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { - - canvas.drawLine( - Offset( - 0 + _indicatorWidth() / 2, - offset.dx + (configuration.size!.width - _indicatorHeight()) / 2, - ), - Offset( - 0 + _indicatorWidth() / 2, - offset.dx + (configuration.size!.width + _indicatorHeight()) / 2, - ), - _paint..strokeWidth = _indicatorWidth()); - } - - double _indicatorHeight() => - decoration.indicatorHeight ?? _defaultIndicatorHeight; - - double _indicatorWidth() => - decoration.indicatorWidth ?? _defaultIndicatorWidth; -} - -/// TDesign不展示下标 -class TDNoneIndicator extends Decoration { - @override - BoxPainter createBoxPainter([VoidCallback? onChanged]) => - _TDNoneIndicatorPainter(); -} - -class _TDNoneIndicatorPainter extends BoxPainter { - @override - void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {} -} diff --git a/lib/src/components/tabbar/td_tab_bar_vertical_view.dart b/lib/src/components/tabbar/td_tab_bar_vertical_view.dart deleted file mode 100644 index f6114c7da..000000000 --- a/lib/src/components/tabbar/td_tab_bar_vertical_view.dart +++ /dev/null @@ -1,216 +0,0 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; - -class TDTabBarVerticalView extends StatefulWidget { - const TDTabBarVerticalView({ - Key? key, - required this.children, - this.controller, - this.physics, - this.dragStartBehavior = DragStartBehavior.start, - }) : super(key: key); - - final TabController? controller; - - final List children; - - final ScrollPhysics? physics; - - final DragStartBehavior dragStartBehavior; - - @override - State createState() => _TDTabBarVerticalViewState(); -} - -class _TDTabBarVerticalViewState extends State { - TabController? _controller; - late PageController _pageController; - late List _children; - late List _childrenWithKey; - int? _currentIndex; - int _warpUnderwayCount = 0; - - // If the TabBarView is rebuilt with a new tab controller, the caller should - // dispose the old one. In that case the old controller's animation will be - // null and should not be accessed. - bool get _controllerIsValid => _controller?.animation != null; - - void _updateTabController() { - final newController = widget.controller ?? DefaultTabController.of(context); - assert(() { - if (newController == null) { - throw FlutterError( - 'No TabController for ${widget.runtimeType}.\n' - 'When creating a ${widget.runtimeType}, you must either provide an explicit ' - 'TabController using the "controller" property, or you must ensure that there ' - 'is a DefaultTabController above the ${widget.runtimeType}.\n' - 'In this case, there was neither an explicit controller nor a default controller.', - ); - } - return true; - }()); - - if (newController == _controller) { - return; - } - - if (_controllerIsValid) { - _controller!.animation!.removeListener(_handleTabControllerAnimationTick); - } - _controller = newController; - if (_controller != null) { - _controller!.animation!.addListener(_handleTabControllerAnimationTick); - } - } - - @override - void initState() { - super.initState(); - _updateChildren(); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - _updateTabController(); - _currentIndex = _controller?.index; - _pageController = PageController(initialPage: _currentIndex ?? 0); - } - - @override - void didUpdateWidget(TDTabBarVerticalView oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.controller != oldWidget.controller) { - _updateTabController(); - } - if (widget.children != oldWidget.children && _warpUnderwayCount == 0) { - _updateChildren(); - } - } - - @override - void dispose() { - if (_controllerIsValid) { - _controller!.animation!.removeListener(_handleTabControllerAnimationTick); - } - _controller = null; - // We don't own the _controller Animation, so it's not disposed here. - super.dispose(); - } - - void _updateChildren() { - _children = widget.children; - _childrenWithKey = KeyedSubtree.ensureUniqueKeysForList(widget.children); - } - - void _handleTabControllerAnimationTick() { - if (_warpUnderwayCount > 0 || !_controller!.indexIsChanging) { - return; - } // This widget is driving the controller's animation. - - if (_controller!.index != _currentIndex) { - _currentIndex = _controller!.index; - _warpToCurrentIndex(); - } - } - - Future _warpToCurrentIndex() async { - if (!mounted) { - return Future.value(); - } - - if (_pageController.page == _currentIndex!.toDouble()) { - return Future.value(); - } - - final previousIndex = _controller!.previousIndex; - if ((_currentIndex! - previousIndex).abs() == 1) { - _warpUnderwayCount += 1; - await _pageController.animateToPage(_currentIndex!, duration: kTabScrollDuration, curve: Curves.ease); - _warpUnderwayCount -= 1; - return Future.value(); - } - - assert((_currentIndex! - previousIndex).abs() > 1); - final initialPage = _currentIndex! > previousIndex - ? _currentIndex! - 1 - : _currentIndex! + 1; - final originalChildren = _childrenWithKey; - setState(() { - _warpUnderwayCount += 1; - - _childrenWithKey = List.from(_childrenWithKey, growable: false); - final temp = _childrenWithKey[initialPage]; - _childrenWithKey[initialPage] = _childrenWithKey[previousIndex]; - _childrenWithKey[previousIndex] = temp; - }); - _pageController.jumpToPage(initialPage); - - await _pageController.animateToPage(_currentIndex!, duration: kTabScrollDuration, curve: Curves.ease); - if (!mounted) { - return Future.value(); - } - setState(() { - _warpUnderwayCount -= 1; - if (widget.children != _children) { - _updateChildren(); - } else { - _childrenWithKey = originalChildren; - } - }); - } - - // Called when the PageView scrolls - bool _handleScrollNotification(ScrollNotification notification) { - if (_warpUnderwayCount > 0) { - return false; - } - - if (notification.depth != 0) { - return false; - } - - _warpUnderwayCount += 1; - if (notification is ScrollUpdateNotification && !_controller!.indexIsChanging) { - if ((_pageController.page! - _controller!.index).abs() > 1.0) { - _controller!.index = _pageController.page!.round(); - _currentIndex =_controller!.index; - } - _controller!.offset = (_pageController.page! - _controller!.index).clamp(-1.0, 1.0); - } else if (notification is ScrollEndNotification) { - _controller!.index = _pageController.page!.round(); - _currentIndex = _controller!.index; - if (!_controller!.indexIsChanging) { - _controller!.offset = (_pageController.page! - _controller!.index).clamp(-1.0, 1.0); - } - } - _warpUnderwayCount -= 1; - - return false; - } - - @override - Widget build(BuildContext context) { - assert(() { - if (_controller!.length != widget.children.length) { - throw FlutterError( - "Controller's length property (${_controller!.length}) does not match the " - "number of tabs (${widget.children.length}) present in TabBar's tabs property.", - ); - } - return true; - }()); - return NotificationListener( - onNotification: _handleScrollNotification, - child: PageView( - scrollDirection: Axis.vertical, - dragStartBehavior: widget.dragStartBehavior, - controller: _pageController, - physics: widget.physics == null - ? const NeverScrollableScrollPhysics() - : const PageScrollPhysics().applyTo(widget.physics), - children: _childrenWithKey, - ), - ); - } -} diff --git a/lib/src/components/tabbar/td_vertical_tab_bar.dart b/lib/src/components/tabbar/td_vertical_tab_bar.dart deleted file mode 100644 index 0598c1610..000000000 --- a/lib/src/components/tabbar/td_vertical_tab_bar.dart +++ /dev/null @@ -1,940 +0,0 @@ -import 'dart:math' as math; -import 'dart:ui' show lerpDouble; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart' show DragStartBehavior; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; - - -const double _kTabHeight = 54.0; -const double _kTextAndIconTabHeight = 80.0; - -enum TabBarIndicatorSize { - tab, - label, -} - -class Tab extends StatelessWidget implements PreferredSizeWidget { - const Tab({ - Key? key, - this.text, - this.icon, - this.iconMargin = const EdgeInsets.only(bottom: 10.0), - this.height, - this.child, - }) : assert(text != null || child != null || icon != null), - assert(text == null || child == null), - super(key: key); - - final String? text; - final Widget? child; - final Widget? icon; - final EdgeInsetsGeometry iconMargin; - final double? height; - - Widget _buildLabelText() { - return child ?? Text(text!, softWrap: false, overflow: TextOverflow.fade); - } - - @override - Widget build(BuildContext context) { - assert(debugCheckHasMaterial(context)); - - final double calculatedHeight; - final Widget label; - if (icon == null) { - calculatedHeight = _kTabHeight; - label = _buildLabelText(); - } else if (text == null && child == null) { - calculatedHeight = _kTabHeight; - label = icon!; - } else { - calculatedHeight = _kTextAndIconTabHeight; - label = Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - margin: iconMargin, - child: icon, - ), - _buildLabelText(), - ], - ); - } - - return SizedBox( - height: height ?? calculatedHeight, - child: Center( - widthFactor: 1.0, - child: label, - ), - ); - } - - @override - void debugFillProperties(DiagnosticPropertiesBuilder properties) { - super.debugFillProperties(properties); - properties.add(StringProperty('text', text, defaultValue: null)); - properties.add(DiagnosticsProperty('icon', icon, defaultValue: null)); - } - - @override - Size get preferredSize { - if (height != null) { - return Size.fromHeight(height!); - } else if ((text != null || child != null) && icon != null) { - return const Size.fromHeight(_kTextAndIconTabHeight); - } else { - return const Size.fromHeight(_kTabHeight); - } - } -} - -class _TabStyle extends AnimatedWidget { - const _TabStyle({ - Key? key, - required Animation animation, - required this.selected, - required this.labelColor, - required this.unselectedLabelColor, - required this.labelStyle, - required this.unselectedLabelStyle, - required this.child, - required this.selectedBackgroundColor, - required this.unSelectedBackgroundColor, - }) : super(key: key, listenable: animation); - - final TextStyle? labelStyle; - final TextStyle? unselectedLabelStyle; - final bool selected; - final Color? labelColor; - final Color? unselectedLabelColor; - final Widget child; - final Color? selectedBackgroundColor; - final Color? unSelectedBackgroundColor; - - @override - Widget build(BuildContext context) { - final themeData = Theme.of(context); - final tabBarTheme = TabBarTheme.of(context); - final animation = listenable as Animation; - - // To enable TextStyle.lerp(style1, style2, value), both styles must have - // the same value of inherit. Force that to be inherit=true here. - final defaultStyle = (labelStyle - ?? tabBarTheme.labelStyle - ?? themeData.primaryTextTheme.bodyText1! - ).copyWith(inherit: true); - final defaultUnselectedStyle = (unselectedLabelStyle - ?? tabBarTheme.unselectedLabelStyle - ?? labelStyle - ?? themeData.primaryTextTheme.bodyText1! - ).copyWith(inherit: true); - final textStyle = selected - ? TextStyle.lerp(defaultStyle, defaultUnselectedStyle, animation.value)! - : TextStyle.lerp(defaultUnselectedStyle, defaultStyle, animation.value)!; - - final selectedColor = labelColor - ?? tabBarTheme.labelColor - ?? themeData.primaryTextTheme.bodyText1!.color!; - final unselectedColor = unselectedLabelColor - ?? tabBarTheme.unselectedLabelColor - ?? selectedColor.withAlpha(0xB2); // 70% alpha - final color = selected - ? Color.lerp(selectedColor, unselectedColor, animation.value)! - : Color.lerp(unselectedColor, selectedColor, animation.value)!; - final backgroundColor = selected? selectedBackgroundColor ?? Colors.white : unSelectedBackgroundColor ?? const Color(0xFFF3F3F3); - - return Container( - color: backgroundColor, - child: DefaultTextStyle( - style: textStyle.copyWith(color: color), - child: IconTheme.merge( - data: IconThemeData( - size: 24.0, - color: color, - ), - child: child, - ), - ), - ); - } -} - -typedef _LayoutCallback = void Function(List yOffsets, TextDirection textDirection, double width); - -class _TabLabelBarRenderer extends RenderFlex { - _TabLabelBarRenderer({ - List? children, - required Axis direction, - required MainAxisSize mainAxisSize, - required MainAxisAlignment mainAxisAlignment, - required CrossAxisAlignment crossAxisAlignment, - required TextDirection textDirection, - required VerticalDirection verticalDirection, - required this.onPerformLayout, - }) : super( - children: children, - direction: direction, - mainAxisSize: mainAxisSize, - mainAxisAlignment: mainAxisAlignment, - crossAxisAlignment: crossAxisAlignment, - textDirection: textDirection, - verticalDirection: verticalDirection, - ); - - _LayoutCallback onPerformLayout; - - @override - void performLayout() { - super.performLayout(); - var child = firstChild; - final yOffsets = []; - while (child != null) { - final childParentData = child.parentData! as FlexParentData; - yOffsets.add(childParentData.offset.dy); - assert(child.parentData == childParentData); - child = childParentData.nextSibling; - } - assert(textDirection != null); - switch (textDirection!) { - case TextDirection.rtl: - yOffsets.insert(0, size.height); - break; - case TextDirection.ltr: - yOffsets.add(size.height); - break; - } - onPerformLayout(yOffsets, textDirection!, size.height); - } -} - -class _TabLabelBar extends Flex { - _TabLabelBar({ - Key? key, - List children = const [], - required this.onPerformLayout, - }) : super( - key: key, - children: children, - direction: Axis.vertical, - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - verticalDirection: VerticalDirection.down, - ); - - final _LayoutCallback onPerformLayout; - - @override - RenderFlex createRenderObject(BuildContext context) { - return _TabLabelBarRenderer( - direction: direction, - mainAxisAlignment: mainAxisAlignment, - mainAxisSize: mainAxisSize, - crossAxisAlignment: crossAxisAlignment, - textDirection: getEffectiveTextDirection(context)!, - verticalDirection: verticalDirection, - onPerformLayout: onPerformLayout, - ); - } - - @override - void updateRenderObject(BuildContext context, _TabLabelBarRenderer renderObject) { - super.updateRenderObject(context, renderObject); - renderObject.onPerformLayout = onPerformLayout; - } -} - -double _indexChangeProgress(TabController controller) { - final controllerValue = controller.animation!.value; - final previousIndex = controller.previousIndex.toDouble(); - final currentIndex = controller.index.toDouble(); - - if (!controller.indexIsChanging) { - return (currentIndex - controllerValue).abs().clamp(0.0, 1.0); - } - - return (controllerValue - currentIndex).abs() / (currentIndex - previousIndex).abs(); -} - -class _IndicatorPainter extends CustomPainter { - _IndicatorPainter({ - required this.controller, - required this.indicator, - required this.indicatorSize, - required this.tabKeys, - required _IndicatorPainter? old, - required this.indicatorPadding, - }) : super(repaint: controller.animation) { - if (old != null) { - saveTabOffsets(old._currentTabOffsets, old._currentTextDirection); - } - } - - final TabController controller; - final Decoration indicator; - final TabBarIndicatorSize? indicatorSize; - final EdgeInsetsGeometry indicatorPadding; - final List tabKeys; - - List? _currentTabOffsets; - TextDirection? _currentTextDirection; - - Rect? _currentRect; - BoxPainter? _painter; - bool _needsPaint = false; - void markNeedsPaint() { - _needsPaint = true; - } - - void dispose() { - _painter?.dispose(); - } - - void saveTabOffsets(List? tabOffsets, TextDirection? textDirection) { - _currentTabOffsets = tabOffsets; - _currentTextDirection = textDirection; - } - - int get maxTabIndex => _currentTabOffsets!.length - 2; - - double centerOf(int tabIndex) { - assert(_currentTabOffsets != null); - assert(_currentTabOffsets!.isNotEmpty); - assert(tabIndex >= 0); - assert(tabIndex <= maxTabIndex); - return (_currentTabOffsets![tabIndex] + _currentTabOffsets![tabIndex + 1]) / 2.0; - } - - Rect indicatorRect(Size tabBarSize, int tabIndex) { - assert(_currentTabOffsets != null); - assert(_currentTextDirection != null); - assert(_currentTabOffsets!.isNotEmpty); - assert(tabIndex >= 0); - assert(tabIndex <= maxTabIndex); - double tabLeft, tabRight; - switch (_currentTextDirection!) { - case TextDirection.rtl: - tabLeft = _currentTabOffsets![tabIndex + 1]; - tabRight = _currentTabOffsets![tabIndex]; - break; - case TextDirection.ltr: - tabLeft = _currentTabOffsets![tabIndex]; - tabRight = _currentTabOffsets![tabIndex + 1]; - break; - } - - if (indicatorSize == TabBarIndicatorSize.label) { - final tabHeight = tabKeys[tabIndex].currentContext!.size!.height; - final delta = ((tabRight - tabLeft) - tabHeight) / 2.0; - tabLeft += delta; - tabRight -= delta; - } - - final insets = indicatorPadding.resolve(_currentTextDirection); - final rect = Rect.fromLTWH(tabLeft, 0.0, tabRight - tabLeft, tabBarSize.height); - - if (!(rect.size >= insets.collapsedSize)) { - throw FlutterError( - 'indicatorPadding insets should be less than Tab Size\n' - 'Rect Size : ${rect.size}, Insets: ${insets.toString()}', - ); - } - return insets.deflateRect(rect); - } - - @override - void paint(Canvas canvas, Size size) { - _needsPaint = false; - _painter ??= indicator.createBoxPainter(markNeedsPaint); - - final index = controller.index.toDouble(); - final value = controller.animation!.value; - final ltr = index > value; - final from = (ltr ? value.floor() : value.ceil()).clamp(0, maxTabIndex); - final to = (ltr ? from + 1 : from - 1).clamp(0, maxTabIndex); - final fromRect = indicatorRect(size, from); - final toRect = indicatorRect(size, to); - _currentRect = Rect.lerp(fromRect, toRect, (value - from).abs()); - assert(_currentRect != null); - - final configuration = ImageConfiguration( - size: _currentRect!.size, - textDirection: _currentTextDirection, - ); - _painter!.paint(canvas, _currentRect!.topLeft, configuration); - } - - @override - bool shouldRepaint(_IndicatorPainter old) { - return _needsPaint - || controller != old.controller - || indicator != old.indicator - || tabKeys.length != old.tabKeys.length - || (!listEquals(_currentTabOffsets, old._currentTabOffsets)) - || _currentTextDirection != old._currentTextDirection; - } -} - -class _ChangeAnimation extends Animation with AnimationWithParentMixin { - _ChangeAnimation(this.controller); - - final TabController controller; - - @override - Animation get parent => controller.animation!; - - @override - void removeStatusListener(AnimationStatusListener listener) { - if (controller.animation != null) { - super.removeStatusListener(listener); - } - } - - @override - void removeListener(VoidCallback listener) { - if (controller.animation != null) { - super.removeListener(listener); - } - } - - @override - double get value => _indexChangeProgress(controller); -} - -class _DragAnimation extends Animation with AnimationWithParentMixin { - _DragAnimation(this.controller, this.index); - - final TabController controller; - final int index; - - @override - Animation get parent => controller.animation!; - - @override - void removeStatusListener(AnimationStatusListener listener) { - if (controller.animation != null) { - super.removeStatusListener(listener); - } - } - - @override - void removeListener(VoidCallback listener) { - if (controller.animation != null) { - super.removeListener(listener); - } - } - - @override - double get value { - assert(!controller.indexIsChanging); - final controllerMaxValue = (controller.length - 1).toDouble(); - final controllerValue = controller.animation!.value.clamp(0.0, controllerMaxValue); - return (controllerValue - index.toDouble()).abs().clamp(0.0, 1.0); - } -} - -class _TabBarScrollPosition extends ScrollPositionWithSingleContext { - _TabBarScrollPosition({ - required ScrollPhysics physics, - required ScrollContext context, - required ScrollPosition? oldPosition, - required this.tabBar, - }) : super( - physics: physics, - context: context, - initialPixels: null, - oldPosition: oldPosition, - ); - - final _TabBarState tabBar; - - bool? _initialViewportDimensionWasZero; - - @override - bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) { - var result = true; - if (_initialViewportDimensionWasZero != true) { - _initialViewportDimensionWasZero = viewportDimension != 0.0; - correctPixels(tabBar._initialScrollOffset(viewportDimension, minScrollExtent, maxScrollExtent)); - result = false; - } - return super.applyContentDimensions(minScrollExtent, maxScrollExtent) && result; - } -} - -class _TabBarScrollController extends ScrollController { - _TabBarScrollController(this.tabBar); - - final _TabBarState tabBar; - - @override - ScrollPosition createScrollPosition(ScrollPhysics physics, ScrollContext context, ScrollPosition? oldPosition) { - return _TabBarScrollPosition( - physics: physics, - context: context, - oldPosition: oldPosition, - tabBar: tabBar, - ); - } -} - -class VerticalTabBar extends StatefulWidget implements PreferredSizeWidget { - const VerticalTabBar({ - Key? key, - required this.tabs, - this.selectedBackgroundColor, - this.unSelectedBackgroundColor, - this.controller, - this.isScrollable = false, - this.padding, - this.indicatorColor, - this.automaticIndicatorColorAdjustment = true, - this.indicatorWeight = 2.0, - this.indicatorPadding = EdgeInsets.zero, - this.indicator, - this.indicatorSize, - this.labelColor, - this.labelStyle, - this.labelPadding, - this.unselectedLabelColor, - this.unselectedLabelStyle, - this.dragStartBehavior = DragStartBehavior.start, - this.overlayColor, - this.mouseCursor, - this.enableFeedback, - this.onTap, - this.physics, - }) : assert(indicator != null || (indicatorWeight > 0.0)), - super(key: key); - - final Color? selectedBackgroundColor; - - final Color? unSelectedBackgroundColor; - - final List tabs; - - final TabController? controller; - - final bool isScrollable; - - final EdgeInsetsGeometry? padding; - - final Color? indicatorColor; - - final double indicatorWeight; - - final EdgeInsetsGeometry indicatorPadding; - - final Decoration? indicator; - - final bool automaticIndicatorColorAdjustment; - - final TabBarIndicatorSize? indicatorSize; - - final Color? labelColor; - - final Color? unselectedLabelColor; - - final TextStyle? labelStyle; - - final EdgeInsetsGeometry? labelPadding; - - final TextStyle? unselectedLabelStyle; - - final MaterialStateProperty? overlayColor; - - final DragStartBehavior dragStartBehavior; - - final MouseCursor? mouseCursor; - - final bool? enableFeedback; - - final ValueChanged? onTap; - - final ScrollPhysics? physics; - - @override - Size get preferredSize { - var maxHeight = _kTabHeight; - for (final item in tabs) { - if (item is PreferredSizeWidget) { - final itemHeight = item.preferredSize.height; - maxHeight = math.max(itemHeight, maxHeight); - } - } - return Size.fromHeight(maxHeight); - } - - bool get tabHasTextAndIcon { - for (final item in tabs) { - if (item is PreferredSizeWidget) { - if (item.preferredSize.height == _kTextAndIconTabHeight) { - return true; - } - } - } - return false; - } - - @override - State createState() => _TabBarState(); -} - -class _TabBarState extends State { - ScrollController? _scrollController; - TabController? _controller; - _IndicatorPainter? _indicatorPainter; - int? _currentIndex; - late double _tabStripWidth; - late List _tabKeys; - - @override - void initState() { - super.initState(); - _tabKeys = widget.tabs.map((Widget tab) => GlobalKey()).toList(); - } - - Decoration get _indicator { - if (widget.indicator != null) { - return widget.indicator!; - } - final tabBarTheme = TabBarTheme.of(context); - if (tabBarTheme.indicator != null) { - return tabBarTheme.indicator!; - } - - var color = widget.indicatorColor ?? Theme.of(context).indicatorColor; - if (widget.automaticIndicatorColorAdjustment && color.value == Material.of(context)?.color?.value) { - color = Colors.white; - } - - return UnderlineTabIndicator( - borderSide: BorderSide( - width: widget.indicatorWeight, - color: color, - ), - ); - } - - bool get _controllerIsValid => _controller?.animation != null; - - void _updateTabController() { - final newController = widget.controller ?? DefaultTabController.of(context); - assert(() { - if (newController == null) { - throw FlutterError( - 'No TabController for ${widget.runtimeType}.\n' - 'When creating a ${widget.runtimeType}, you must either provide an explicit ' - 'TabController using the "controller" property, or you must ensure that there ' - 'is a DefaultTabController above the ${widget.runtimeType}.\n' - 'In this case, there was neither an explicit controller nor a default controller.', - ); - } - return true; - }()); - - if (newController == _controller) { - return; - } - - if (_controllerIsValid) { - _controller!.animation!.removeListener(_handleTabControllerAnimationTick); - _controller!.removeListener(_handleTabControllerTick); - } - _controller = newController; - if (_controller != null) { - _controller!.animation!.addListener(_handleTabControllerAnimationTick); - _controller!.addListener(_handleTabControllerTick); - _currentIndex = _controller!.index; - } - } - - void _initIndicatorPainter() { - _indicatorPainter = !_controllerIsValid ? null : _IndicatorPainter( - controller: _controller!, - indicator: _indicator, - indicatorSize: widget.indicatorSize ?? TabBarIndicatorSize.label, - indicatorPadding: widget.indicatorPadding, - tabKeys: _tabKeys, - old: _indicatorPainter, - ); - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - assert(debugCheckHasMaterial(context)); - _updateTabController(); - _initIndicatorPainter(); - } - - @override - void didUpdateWidget(VerticalTabBar oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.controller != oldWidget.controller) { - _updateTabController(); - _initIndicatorPainter(); - } else if (widget.indicatorColor != oldWidget.indicatorColor || - widget.indicatorWeight != oldWidget.indicatorWeight || - widget.indicatorSize != oldWidget.indicatorSize || - widget.indicator != oldWidget.indicator) { - _initIndicatorPainter(); - } - - if (widget.tabs.length > oldWidget.tabs.length) { - final delta = widget.tabs.length - oldWidget.tabs.length; - _tabKeys.addAll(List.generate(delta, (int n) => GlobalKey())); - } else if (widget.tabs.length < oldWidget.tabs.length) { - _tabKeys.removeRange(widget.tabs.length, oldWidget.tabs.length); - } - } - - @override - void dispose() { - _indicatorPainter!.dispose(); - if (_controllerIsValid) { - _controller!.animation!.removeListener(_handleTabControllerAnimationTick); - _controller!.removeListener(_handleTabControllerTick); - } - _controller = null; - // We don't own the _controller Animation, so it's not disposed here. - super.dispose(); - } - - int get maxTabIndex => _indicatorPainter!.maxTabIndex; - - double _tabScrollOffset(int index, double viewportWidth, double minExtent, double maxExtent) { - if (!widget.isScrollable) { - return 0.0; - } - var tabCenter = _indicatorPainter!.centerOf(index); - switch (Directionality.of(context)) { - case TextDirection.rtl: - tabCenter = _tabStripWidth - tabCenter; - break; - case TextDirection.ltr: - break; - } - return (tabCenter - viewportWidth / 2.0).clamp(minExtent, maxExtent); - } - - double _tabCenteredScrollOffset(int index) { - final position = _scrollController!.position; - return _tabScrollOffset(index, position.viewportDimension, position.minScrollExtent, position.maxScrollExtent); - } - - double _initialScrollOffset(double viewportWidth, double minExtent, double maxExtent) { - return _tabScrollOffset(_currentIndex!, viewportWidth, minExtent, maxExtent); - } - - void _scrollToCurrentIndex() { - final offset = _tabCenteredScrollOffset(_currentIndex!); - _scrollController!.animateTo(offset, duration: kTabScrollDuration, curve: Curves.ease); - } - - void _scrollToControllerValue() { - final leadingPosition = _currentIndex! > 0 ? _tabCenteredScrollOffset(_currentIndex! - 1) : null; - final middlePosition = _tabCenteredScrollOffset(_currentIndex!); - final trailingPosition = _currentIndex! < maxTabIndex ? _tabCenteredScrollOffset(_currentIndex! + 1) : null; - - final index = _controller!.index.toDouble(); - final value = _controller!.animation!.value; - final double offset; - if (value == index - 1.0) { - offset = leadingPosition ?? middlePosition; - } else if (value == index + 1.0) { - offset = trailingPosition ?? middlePosition; - } else if (value == index) { - offset = middlePosition; - } else if (value < index) { - offset = leadingPosition == null ? middlePosition : lerpDouble(middlePosition, leadingPosition, index - value)!; - } else { - offset = trailingPosition == null ? middlePosition : lerpDouble(middlePosition, trailingPosition, value - index)!; - } - - _scrollController!.jumpTo(offset); - } - - void _handleTabControllerAnimationTick() { - assert(mounted); - if (!_controller!.indexIsChanging && widget.isScrollable) { - // Sync the TabBar's scroll position with the TabBarView's PageView. - _currentIndex = _controller!.index; - _scrollToControllerValue(); - } - } - - void _handleTabControllerTick() { - if (_controller!.index != _currentIndex) { - _currentIndex = _controller!.index; - if (widget.isScrollable) { - _scrollToCurrentIndex(); - } - } - setState(() {}); - } - - void _saveTabOffsets(List tabOffsets, TextDirection textDirection, double width) { - _tabStripWidth = width; - _indicatorPainter?.saveTabOffsets(tabOffsets, textDirection); - } - - void _handleTap(int index) { - assert(index >= 0 && index < widget.tabs.length); - _controller!.animateTo(index); - widget.onTap?.call(index); - } - - Widget _buildStyledTab(Widget child, bool selected, Animation animation) { - return _TabStyle( - animation: animation, - selected: selected, - selectedBackgroundColor: widget.selectedBackgroundColor, - unSelectedBackgroundColor: widget.unSelectedBackgroundColor, - labelColor: widget.labelColor, - unselectedLabelColor: widget.unselectedLabelColor, - labelStyle: widget.labelStyle, - unselectedLabelStyle: widget.unselectedLabelStyle, - child: child, - ); - } - - @override - Widget build(BuildContext context) { - assert(debugCheckHasMaterialLocalizations(context)); - assert(() { - if (_controller!.length != widget.tabs.length) { - throw FlutterError( - "Controller's length property (${_controller!.length}) does not match the " - "number of tabs (${widget.tabs.length}) present in TabBar's tabs property.", - ); - } - return true; - }()); - final localizations = MaterialLocalizations.of(context); - if (_controller!.length == 0) { - return Container( - height: _kTabHeight, - ); - } - - final tabBarTheme = TabBarTheme.of(context); - - final wrappedTabs = List.generate(widget.tabs.length, (int index) { - const verticalAdjustment = (_kTextAndIconTabHeight - _kTabHeight)/2.0; - EdgeInsetsGeometry? adjustedPadding; - - if (widget.tabs[index] is PreferredSizeWidget) { - final tab = widget.tabs[index] as PreferredSizeWidget; - if (widget.tabHasTextAndIcon && tab.preferredSize.height == _kTabHeight) { - if (widget.labelPadding != null || tabBarTheme.labelPadding != null) { - adjustedPadding = (widget.labelPadding ?? tabBarTheme.labelPadding!).add(const EdgeInsets.symmetric(vertical: verticalAdjustment)); - } - else { - adjustedPadding = const EdgeInsets.symmetric(vertical: verticalAdjustment, horizontal: 16.0); - } - } - } - - return Center( - child: Padding( - padding: adjustedPadding ?? widget.labelPadding ?? tabBarTheme.labelPadding ?? kTabLabelPadding, - child: KeyedSubtree( - key: _tabKeys[index], - child: widget.tabs[index], - ), - ), - ); - }); - - if (_controller != null) { - final previousIndex = _controller!.previousIndex; - - if (_controller!.indexIsChanging) { - // The user tapped on a tab, the tab controller's animation is running. - assert(_currentIndex != previousIndex); - final Animation animation = _ChangeAnimation(_controller!); - wrappedTabs[_currentIndex!] = _buildStyledTab(wrappedTabs[_currentIndex!], true, animation); - wrappedTabs[previousIndex] = _buildStyledTab(wrappedTabs[previousIndex], false, animation); - } else { - // The user is dragging the TabBarView's PageView left or right. - final tabIndex = _currentIndex!; - final Animation centerAnimation = _DragAnimation(_controller!, tabIndex); - wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], true, centerAnimation); - if (_currentIndex! > 0) { - final tabIndex = _currentIndex! - 1; - final Animation previousAnimation = ReverseAnimation(_DragAnimation(_controller!, tabIndex)); - wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], false, previousAnimation); - } - if (_currentIndex! < widget.tabs.length - 1) { - final tabIndex = _currentIndex! + 1; - final Animation nextAnimation = ReverseAnimation(_DragAnimation(_controller!, tabIndex)); - wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], false, nextAnimation); - } - } - } - - final tabCount = widget.tabs.length; - for (var index = 0; index < tabCount; index += 1) { - wrappedTabs[index] = InkWell( - mouseCursor: widget.mouseCursor ?? SystemMouseCursors.click, - onTap: () { _handleTap(index); }, - enableFeedback: widget.enableFeedback ?? true, - overlayColor: widget.overlayColor, - child: Stack( - children: [ - wrappedTabs[index], - Semantics( - selected: index == _currentIndex, - label: localizations.tabLabel(tabIndex: index + 1, tabCount: tabCount), - ), - ], - ), - ); - if (!widget.isScrollable) { - wrappedTabs[index] = Expanded(child: wrappedTabs[index]); - } - } - - Widget tabBar = CustomPaint( - foregroundPainter: _indicatorPainter, - child: _TabStyle( - selectedBackgroundColor: widget.selectedBackgroundColor, - unSelectedBackgroundColor: widget.unSelectedBackgroundColor, - animation: kAlwaysDismissedAnimation, - selected: false, - labelColor: widget.labelColor, - unselectedLabelColor: widget.unselectedLabelColor, - labelStyle: widget.labelStyle, - unselectedLabelStyle: widget.unselectedLabelStyle, - child: _TabLabelBar( - onPerformLayout: _saveTabOffsets, - children: wrappedTabs, - ), - ), - ); - - if (widget.isScrollable) { - _scrollController ??= _TabBarScrollController(this); - tabBar = SingleChildScrollView( - dragStartBehavior: widget.dragStartBehavior, - scrollDirection: Axis.vertical, - controller: _scrollController, - padding: widget.padding, - physics: widget.physics, - child: tabBar, - ); - } else if (widget.padding != null) { - tabBar = Padding( - padding: widget.padding!, - child: tabBar, - ); - } - - return tabBar; - } -} - diff --git a/lib/src/components/tag/td_tag.dart b/lib/src/components/tag/td_tag.dart deleted file mode 100644 index 150062920..000000000 --- a/lib/src/components/tag/td_tag.dart +++ /dev/null @@ -1,254 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; - -/// 标签尺寸 -enum TDTagSize { large, middle, small, custom } - -/// 展示型标签组件,仅展示,内部不可更改自身状态 -/// 支持样式:方形/圆角/半圆/带关闭图标 -/// -class TDTag extends StatelessWidget { - - const TDTag(this.text, - { - this.textColor, - this.backgroundColor, - this.font, - this.fontWeight, - this.style, - this.size = TDTagSize.middle, - this.padding, - this.forceVerticalCenter = true, - this.needCloseIcon = false, - this.onCloseTap, - this.overflow, - Key? key}) - : super(key: key); - - /// 标签内容 - final String text; - - /// 文字颜色, 优先级高于style的textColor - final Color? textColor; - - /// 背景颜色, 优先级高于style的backgroundColor - final Color? backgroundColor; - - /// 字体尺寸, 优先级高于style的font - final Font? font; - - /// 字体粗细, 优先级高于style的fontWeight - final FontWeight? fontWeight; - - /// 标签样式 - final TDTagStyle? style; - - /// 标签大小 - final TDTagSize size; - - /// 自定义模式下的间距 - final EdgeInsets? padding; - - /// 是否强制中文文字居中 - final bool forceVerticalCenter; - - /// 关闭图标 - final bool needCloseIcon; - - /// 文字溢出处理 - final TextOverflow? overflow; - - /// 关闭图标点击事件 - final GestureTapCallback? onCloseTap; - - @override - Widget build(BuildContext context) { - var innerStyle = style ?? RoundRectTagStyle(context: context); - - Widget child = TDText( - text, - overflow: overflow, - forceVerticalCenter: forceVerticalCenter, - textColor: textColor ?? innerStyle.getTextColor, - font: font ?? innerStyle.font ?? _getFont(context), - fontWeight: fontWeight ?? innerStyle.fontWeight, - ); - - if(needCloseIcon){ - child = Row( - mainAxisSize: MainAxisSize.min, - children: [ - child, - GestureDetector( - onTap: onCloseTap, - child: Container( - margin: const EdgeInsets.only(left: 2), - child: Icon(TDIcons.close, color: innerStyle.getTextColor, size: 12,), - ), - ), - ],); - } - - return Container( - padding: padding ?? _getPadding(), - decoration: BoxDecoration( - color: backgroundColor ?? innerStyle.getBackgroundColor, - border: Border.all( - width: innerStyle.border, - color: innerStyle.getWireFrameColor), - borderRadius: innerStyle.getBorderRadius), - child: child, - ); - } - - Font? _getFont(BuildContext context) { - switch (size) { - case TDTagSize.large: - return TDTheme.of(context).fontS; - case TDTagSize.small: - return TDTheme.of(context).fontXXS; - default: - return TDTheme.of(context).fontXS; - } - } - - EdgeInsets _getPadding() { - /// 为了文本居中,修改了padding的值 - switch (size) { - case TDTagSize.large: - return const EdgeInsets.only(left: 15, right: 15, top: 3, bottom: 3); - case TDTagSize.small: - return const EdgeInsets.only(left: 7, right: 7, top: 1, bottom: 1); - default: - return const EdgeInsets.only(left: 11, right: 11, top: 1, bottom: 1); - } - } -} - -/// 点击型标签组件,点击时内部更改自身状态 -/// 支持样式:方形/圆角/半圆/带关闭图标 -class TDSelectTag extends StatefulWidget { - const TDSelectTag(this.text,{ - this.style, - this.unSelectStyle, - this.unEnableSelectStyle, - this.onSelectChanged, - this.isSelected = false, - this.enableSelect = true, - this.size = TDTagSize.middle, - this.padding, - this.forceVerticalCenter = true, - this.needCloseIcon = false, - this.onCloseTap, - Key? key}) : super(key: key); - - - /// 标签内容 - final String text; - - /// 标签样式 - final TDTagStyle? style; - - /// 未选中标签样式 - final TDTagStyle? unSelectStyle; - - /// 不可选标签样式 - final TDTagStyle? unEnableSelectStyle; - - /// 标签点击,选中状态改变时的回调 - final ValueChanged? onSelectChanged; - - /// 是否选中 - final bool isSelected; - - /// 是否可点击选择 - final bool enableSelect; - - /// 标签大小 - final TDTagSize size; - - /// 自定义模式下的间距 - final EdgeInsets? padding; - - /// 是否强制中文文字居中 - final bool forceVerticalCenter; - - /// 关闭图标 - final bool needCloseIcon; - - /// 关闭图标点击事件 - final GestureTapCallback? onCloseTap; - - @override - _TDClickTagState createState() => _TDClickTagState(); -} - -class _TDClickTagState extends State { - - bool _isSelected = false; - - @override - void initState() { - super.initState(); - _isSelected = widget.isSelected; - } - - @override - Widget build(BuildContext context) { - Widget result = TDTag(widget.text, - style: _getStyle(), - size: widget.size, - padding: widget.padding, - forceVerticalCenter: widget.forceVerticalCenter, - needCloseIcon: widget.needCloseIcon, - onCloseTap: widget.onCloseTap,); - if(widget.enableSelect){ - result = GestureDetector( - onTap: (){ - setState(() { - _isSelected = !_isSelected; - widget.onSelectChanged?.call(_isSelected); - }); - }, - child: result, - ); - } - return result; - } - - TDTagStyle? _getStyle() { - if(!widget.enableSelect){ - return _getUnEnableSelectStyle(); - } - return _isSelected ? widget.style : _getUnSelectStyle(); - } - - TDTagStyle _getUnEnableSelectStyle() { - if(widget.unEnableSelectStyle != null){ - return widget.unEnableSelectStyle!; - } - return RoundRectTagStyle( - context: context, - textColor: TDTheme.of(context).fontGyColor4, - backgroundColor: TDTheme.of(context).grayColor3, - ); - } - - TDTagStyle _getUnSelectStyle() { - if(widget.unSelectStyle != null){ - return widget.unSelectStyle!; - } - return RoundRectTagStyle( - context: context, - textColor: TDTheme.of(context).fontGyColor1, - backgroundColor: TDTheme.of(context).grayColor3, - ); - } - - @override - void didUpdateWidget(covariant TDSelectTag oldWidget) { - super.didUpdateWidget(oldWidget); - _isSelected = widget.isSelected; - } -} - diff --git a/lib/src/components/tag/td_tag_styles.dart b/lib/src/components/tag/td_tag_styles.dart deleted file mode 100644 index b4d2f8337..000000000 --- a/lib/src/components/tag/td_tag_styles.dart +++ /dev/null @@ -1,227 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; - -/// 标签样式 -class TDTagStyle { - const TDTagStyle( - {this.context, - this.textColor, - this.backgroundColor, - this.font, - this.fontWeight, - this.border = 0, - this.wireFrameColor, - this.borderRadius}); - - /// 上下文,方便获取主题内容 - final BuildContext? context; - - /// 文字颜色 - final Color? textColor; - - /// 背景颜色 - final Color? backgroundColor; - - /// 边框颜色 - final Color? wireFrameColor; - - /// 圆角 - final BorderRadiusGeometry? borderRadius; - - /// 字体尺寸 - final Font? font; - - /// 字体粗细 - final FontWeight? fontWeight; - - /// 线框粗细 - final double border; - - /// 字体颜色,与属性不同名,方便子类自定义处理 - Color get getTextColor => textColor ?? TDTheme.of(context).fontWhColor1; - - /// 背景颜色,与属性不同名,方便子类自定义处理 - Color get getBackgroundColor => - backgroundColor ?? TDTheme.of(context).brandNormalColor; - - /// 线框颜色,与属性不同名,方便子类自定义处理 - Color get getWireFrameColor => Colors.transparent; - - /// 圆角,,与属性不同名,方便子类自定义处理 - BorderRadiusGeometry get getBorderRadius => BorderRadius.circular(0); -} - - -/// 两端圆弧Style -class CircleRectTagStyle extends TDTagStyle { - CircleRectTagStyle({ - BuildContext? context, - Color? textColor, - Color? backgroundColor, - Font? font, - FontWeight? fontWeight, - }) : super( - context: context, - textColor: textColor, - backgroundColor: backgroundColor, - font: font, - fontWeight: fontWeight, - ); - - @override - BorderRadiusGeometry get getBorderRadius => BorderRadius.circular(1000); -} - -/// 右边端圆弧Style -class SemicircleRectTagStyle extends TDTagStyle { - SemicircleRectTagStyle({ - BuildContext? context, - Color? textColor, - Color? backgroundColor, - Font? font, - FontWeight? fontWeight, - }) : super( - context: context, - textColor: textColor, - backgroundColor: backgroundColor, - font: font, - fontWeight: fontWeight, - ); - - @override - BorderRadiusGeometry get getBorderRadius => - const BorderRadius.horizontal(right: Radius.circular(1000)); -} - - -/// 描边圆角矩形Style -class WireframeRoundRectTagStyle extends TDTagStyle { - /// 圆角半径 - final double radius; - - /// 是否为浅色背景 - final bool isLight; - - const WireframeRoundRectTagStyle({ - BuildContext? context, - Color? textColor, - Color? backgroundColor, - Font? font, - FontWeight? fontWeight, - Color? wireFrameColor, - double border = 1, - this.isLight = false, - this.radius = 2, - }) : super( - context: context, - textColor: textColor, - backgroundColor: backgroundColor, - font: font, - fontWeight: fontWeight, - wireFrameColor: wireFrameColor, - border: border, - ); - - @override - Color get getBackgroundColor { - if (backgroundColor != null) { - return backgroundColor!; - } - return isLight - ? TDTheme.of(context).brandColor2 - : TDTheme.of(context).whiteColor1; - } - - @override - BorderRadiusGeometry get getBorderRadius => BorderRadius.circular(radius); - - @override - Color get getTextColor => textColor ?? TDTheme.of(context).brandNormalColor; - - @override - Color get getWireFrameColor => - wireFrameColor ?? TDTheme.of(context).brandNormalColor; -} - -/// Tag展示类型 -enum TDTagType { - /// 常规 - normal, - - /// 成功 - success, - - /// 警告 - warning, - - /// 错误 - error, - - /// 信息 - message, -} - -/// 常规圆角矩形Style -class RoundRectTagStyle extends WireframeRoundRectTagStyle { - /// 展示类型 - final TDTagType type; - - const RoundRectTagStyle( - {BuildContext? context, - Color? textColor, - Color? backgroundColor, - Font? font, - FontWeight? fontWeight, - this.type = TDTagType.normal, - double radius = 2, - bool isLight = false}) - : super( - context: context, - textColor: textColor, - backgroundColor: backgroundColor, - font: font, - fontWeight: fontWeight, - radius: radius, - isLight: isLight, - border: 0, - ); - - @override - Color get getBackgroundColor { - if (backgroundColor != null) { - return backgroundColor!; - } - if (isLight) { - return TDTheme.of(context).brandColor2; - } - switch (type) { - case TDTagType.normal: - return TDTheme.of(context).brandNormalColor; - case TDTagType.success: - return TDTheme.of(context).successNormalColor; - case TDTagType.warning: - return TDTheme.of(context).warningNormalColor; - case TDTagType.error: - return TDTheme.of(context).errorNormalColor; - case TDTagType.message: - return TDTheme.of(context).grayColor7; - default: - return TDTheme.of(context).whiteColor1; - } - } - - @override - Color get getTextColor { - if (textColor != null) { - return textColor!; - } - if (isLight) { - return TDTheme.of(context).brandNormalColor; - } else { - return TDTheme.of(context).fontWhColor1; - } - } - - @override - Color get getWireFrameColor => Colors.transparent; -} diff --git a/lib/src/components/text/td_text.dart b/lib/src/components/text/td_text.dart deleted file mode 100644 index ce62f5617..000000000 --- a/lib/src/components/text/td_text.dart +++ /dev/null @@ -1,394 +0,0 @@ -import 'dart:math'; -import 'dart:ui' as ui show TextHeightBehavior; - -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; - -import '../../../td_export.dart'; -import '../../util/platform_util.dart'; - -/// 文本控件 -/// 设计原则: -/// 1.为了使用更方便,所以对系统组件进行的扩展,需兼容系统控件所有功能,不能让用户使用TDesign时,因不能满足系统功能而弃用。 -/// 2.非系统已有属性,尽量添加注释 -/// -/// 需求:把一部分在TextStyle中的属性扁平化,放到外层。 -/// 1.暴露系统的所有属性,支持系统所有操作 -/// 2.约束使用主题配置的几种字体 -/// 3.提供转换为系统Text的方法,以使某些系统组件指定接收系统Text时可使用。(Image组件同理) -/// 4.支持自定义TextStyle -/// 5.兼容TextSpan形式 -/// -/// 技巧: -/// 命名参数替换属性的正则: -/// 第一步,把Text中的可选参数拷贝过来,变成如下格式: -/// Text(data, -/// this.style, -/// this.strutStyle, -/// ……) -/// 第二步,正则替换如下: -/// 匹配(前半有默认值,后半无默认值):this\.([a-z|A-Z]+)[ ]*[\=]+[ ]*[a-z|A-Z]+\,|this\.([a-z|A-Z]+)\, -/// 替换:$1$2: this.$1$2, -/// -class TDText extends StatelessWidget { - - const TDText(this.data, - { - this.font, - this.fontWeight = FontWeight.w400, - this.fontFamily, - this.textColor = Colors.black, - this.backgroundColor, - this.isTextThrough = false, - this.lineThroughColor = Colors.white, - this.package, - this.style, - this.strutStyle, - this.textAlign, - this.textDirection, - this.locale, - this.softWrap, - this.overflow, - this.textScaleFactor, - this.maxLines, - this.semanticsLabel, - this.textWidthBasis, - this.textHeightBehavior, - this.forceVerticalCenter = false, - Key? key,}) - : textSpan = null, - super(key: key); - - const TDText.rich( - InlineSpan this.textSpan, { - this.font, - this.fontWeight = FontWeight.w400, - this.fontFamily, - this.textColor = Colors.black, - this.backgroundColor, - this.isTextThrough = false, - this.lineThroughColor = Colors.white, - this.package = 'tdesign_flutter', - Key? key, - this.style, - this.strutStyle, - this.textAlign, - this.textDirection, - this.locale, - this.softWrap, - this.overflow, - this.textScaleFactor, - this.maxLines, - this.semanticsLabel, - this.textWidthBasis, - this.textHeightBehavior, - this.forceVerticalCenter = false, - }) : - data = null, - super(key: key); - - /// 字体尺寸,包含大小size和行高height - final Font? font; - - /// 字体粗细 - final FontWeight? fontWeight; - - /// 字体ttf - final FontFamily? fontFamily; - - /// 文本颜色 - final Color textColor; - - /// 背景颜色 - final Color? backgroundColor; - - /// 字体包名 - final String? package; - - /// 是否是横线穿过样式(删除线) - final bool? isTextThrough; - - /// 删除线颜色,对应TestStyle的decorationColor - final Color? lineThroughColor; - - /// 自定义的TextStyle,其中指定的属性,将覆盖扩展的外层属性 - final TextStyle? style; - - /// 以下系统text属性,释义请参考系统[Text]中注释 - final data; - - final StrutStyle? strutStyle; - - final TextAlign? textAlign; - - final TextDirection? textDirection; - - final Locale? locale; - - final bool? softWrap; - - final TextOverflow? overflow; - - final double? textScaleFactor; - - final int? maxLines; - - final String? semanticsLabel; - - final TextWidthBasis? textWidthBasis; - - final ui.TextHeightBehavior? textHeightBehavior; - - final InlineSpan? textSpan; - - final bool forceVerticalCenter; - - - - - @override - Widget build(BuildContext context) { - if(forceVerticalCenter){ - var config = getConfiguration(context); - var paddingConfig = config?.paddingConfig; - - var textFont = - font ?? TDTheme.of(context).fontM ?? Font(size: 16, lineHeight: 24); - var fontSize = style?.fontSize ?? textFont.size; - var height = style?.height ?? textFont.height; - - paddingConfig ??= TDTextPaddingConfig.getDefaultConfig(); - var showHeight = min(paddingConfig.heightRate, height); - return Container( - color: style?.backgroundColor ?? backgroundColor, - height: fontSize * height, - padding: paddingConfig.getPadding(data, fontSize, height), - child: _getRawText(context: context, textStyle: getTextStyle(context, height: showHeight)), - ); - } - return Container( - color: style?.backgroundColor ?? backgroundColor, - child: _getRawText(context: context), - ); - } - - /// 提取成方法,允许业务定义自己的TDTextConfiguration - TDTextConfiguration? getConfiguration(BuildContext context){ - return context.dependOnInheritedWidgetOfExactType(); - } - - - - TextStyle? getTextStyle(BuildContext? context,{ double? height, Color? backgroundColor}) { - var textFont = - font ?? TDTheme.of(context).fontM ?? Font(size: 16, lineHeight: 24); - return TextStyle( - inherit: style?.inherit ?? true, - color: style?.color ?? textColor, - /// 不使用系统本身的背景色,因为系统属性存在中英文是,会导致颜色出现阶梯状 - backgroundColor: backgroundColor, - fontSize: style?.fontSize ?? textFont.size, - fontWeight: style?.fontWeight ?? fontWeight, - fontStyle: style?.fontStyle, - letterSpacing: style?.letterSpacing, - wordSpacing: style?.wordSpacing, - textBaseline: style?.textBaseline, - height: height ?? style?.height ?? textFont.height, - leadingDistribution: style?.leadingDistribution, - locale: style?.locale, - foreground: style?.foreground, - background: style?.background, - shadows: style?.shadows, - fontFeatures: style?.fontFeatures, - decoration: style?.decoration ?? (isTextThrough! ? TextDecoration.lineThrough : TextDecoration.none), - decorationColor: style?.decorationColor ?? lineThroughColor, - decorationStyle: style?.decorationStyle, - decorationThickness: style?.decorationThickness, - debugLabel: style?.debugLabel, - fontFamily: style?.fontFamily ?? fontFamily?.fontFamily, - fontFamilyFallback: style?.fontFamilyFallback, - package: package, - ); - } - - /// 获取系统原始Text,以便使用到只能接收系统Text组件的地方 - /// 转化为系统原始Text后,将失去padding和background属性 - Text getRawText({BuildContext? context}){ - return _getRawText(context: context, backgroundColor: backgroundColor); - } - - Text _getRawText({BuildContext? context, TextStyle? textStyle, Color? backgroundColor}){ - return textSpan == null ? Text( - data, - key: key, - style: textStyle ?? getTextStyle(context, backgroundColor: backgroundColor), - strutStyle: strutStyle, - textAlign: textAlign, - textDirection: textDirection, - locale: locale, - softWrap: softWrap, - overflow: overflow, - textScaleFactor: textScaleFactor, - maxLines: maxLines, - semanticsLabel: semanticsLabel, - textWidthBasis: textWidthBasis, - textHeightBehavior: textHeightBehavior, - ) : Text.rich(textSpan!, - style: textStyle ?? getTextStyle(context, backgroundColor: backgroundColor), - strutStyle: strutStyle, - textAlign: textAlign, - textDirection: textDirection, - locale: locale, - softWrap: softWrap, - overflow: overflow, - textScaleFactor: textScaleFactor, - maxLines: maxLines, - semanticsLabel: semanticsLabel, - textWidthBasis: textWidthBasis, - textHeightBehavior: textHeightBehavior, - ); - } -} - -/// TextSpan的TDesign扩展,将部分TextStyle中的参数扁平化。 -class TDTextSpan extends TextSpan{ - - /// 构造参数,扩展参数释义可参考[TDText]中字段注释 - TDTextSpan({ - BuildContext? context, // 如果未设置font,且不想使用默认的fontM尺寸时,需设置context,否则可省略 - Font? font, - FontWeight fontWeight = FontWeight.w400, - FontFamily? fontFamily, - Color textColor = Colors.black, - bool? isTextThrough = false, - Color? lineThroughColor = Colors.white, - String package = 'tdesign_flutter', - String? text, - List? children, - TextStyle? style, - GestureRecognizer? recognizer, - MouseCursor? mouseCursor, - PointerEnterEventListener? onEnter, - PointerExitEventListener? onExit, - String? semanticsLabel, - }) : super( - text: text, - children: children, - style: _getTextStyle( - context, - style, - font, - fontWeight, - fontFamily, - textColor, - isTextThrough, - lineThroughColor, - package), - recognizer: recognizer, - mouseCursor: mouseCursor, - onEnter: onEnter, - onExit: onExit, - semanticsLabel: semanticsLabel, - ); - - static TextStyle? _getTextStyle( - BuildContext? context, - TextStyle? style, - Font? font, - FontWeight fontWeight, - FontFamily? fontFamily, - Color textColor, - bool? isTextThrough, - Color? lineThroughColor, - String package, - ) { - var textFont = - font ?? TDTheme.of(context).fontM ?? Font(size: 16, lineHeight: 24); - return TextStyle( - inherit: style?.inherit ?? true, - color: style?.color ?? textColor, - backgroundColor: style?.backgroundColor, - fontSize: style?.fontSize ?? textFont.size, - fontWeight: style?.fontWeight ?? fontWeight, - fontStyle: style?.fontStyle, - letterSpacing: style?.letterSpacing, - wordSpacing: style?.wordSpacing, - textBaseline: style?.textBaseline, - height: style?.height ?? textFont.height, - leadingDistribution: style?.leadingDistribution, - locale: style?.locale, - foreground: style?.foreground, - background: style?.background, - shadows: style?.shadows, - fontFeatures: style?.fontFeatures, - decoration: style?.decoration ?? - (isTextThrough! ? TextDecoration.lineThrough : TextDecoration.none), - decorationColor: style?.decorationColor ?? lineThroughColor, - decorationStyle: style?.decorationStyle, - decorationThickness: style?.decorationThickness, - debugLabel: style?.debugLabel, - fontFamily: style?.fontFamily ?? fontFamily?.fontFamily, - fontFamilyFallback: style?.fontFamilyFallback, - package: package, - ); - } -} - -/// 存储可以自定义TDText居中算法数据的内部控件 -class TDTextConfiguration extends InheritedWidget { - final TDTextPaddingConfig? paddingConfig; - - const TDTextConfiguration( - {this.paddingConfig, Key? key, required Widget child}) - : super(key: key, child: child); - - @override - bool updateShouldNotify(covariant TDTextConfiguration oldWidget) { - return paddingConfig != oldWidget.paddingConfig; - } - - -} - -/// 通过Padding自定义TDText居中算法 -class TDTextPaddingConfig { - - static TDTextPaddingConfig? _defaultConfig; - - /// 获取默认配置 - static TDTextPaddingConfig getDefaultConfig(){ - _defaultConfig ??= TDTextPaddingConfig(); - return _defaultConfig!; - } - - /// 获取padding - EdgeInsetsGeometry getPadding(String data, double fontSize, double height){ - var paddingFont = fontSize * paddingRate; - var paddingLeading ; - if(height < heightRate){ - paddingLeading = 0; - } else { - if(PlatformUtil.isIOS || PlatformUtil.isAndroid){ - paddingLeading = (height * 0.5 - paddingExtraRate) * fontSize; - } else { - paddingLeading = 0; - } - } - var paddingTop = paddingFont + paddingLeading; - if(paddingTop < 0) { - paddingTop = 0; - } - return EdgeInsets.only(top: paddingTop); - } - - /// 以多个汉字测量计算的平均值,Android为Pixel 4模拟器,iOS为iphone 8 plus 模拟器 - double get paddingRate => PlatformUtil.isWeb? 3/8 : PlatformUtil.isAndroid? - 7/128 : 0; - - /// 以多个汉字测量计算的平均值,Android为Pixel 4模拟器,iOS为iphone 8 plus 模拟器 - double get paddingExtraRate => PlatformUtil.isAndroid? 115/256 : 97/240; - - /// height比率,因为设置1时,Android文字可能显示不全,默认为1.1 - double get heightRate => PlatformUtil.isAndroid ? 1.1 : 1; - -} \ No newline at end of file diff --git a/lib/src/components/toast/td_toast.dart b/lib/src/components/toast/td_toast.dart deleted file mode 100644 index 7b487c3b4..000000000 --- a/lib/src/components/toast/td_toast.dart +++ /dev/null @@ -1,214 +0,0 @@ -import 'dart:async'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import '../../../td_export.dart'; -import '../loading/td_circle_indicator.dart'; - -enum IconTextDirection { - horizontal, //横向 - vertical //竖向 -} -class TDToast { - static void showText(String? text, {required BuildContext context, - Duration duration = TDToast._defaultDisPlayDuration}) { - _showOverlay(_TDTextToast(text: text,), context: context); - } - - static void showIconText(String? text, { - IconData? icon, - IconTextDirection direction = IconTextDirection.horizontal, - required BuildContext context, - Duration duration = TDToast._defaultDisPlayDuration}) { - _showOverlay(_TDIconTextToast(text: text, iconData: icon, iconTextDirection: direction,), context: context); - } - - static void showSuccess(String? text, { - IconTextDirection direction = IconTextDirection.horizontal, - required BuildContext context, - Duration duration = TDToast._defaultDisPlayDuration}) { - _showOverlay(_TDIconTextToast(text: text, iconData: TDIcons.check_circle, iconTextDirection: direction,), context: context); - } - - static void showWarning(String? text, { - IconTextDirection direction = IconTextDirection.horizontal, - required BuildContext context, - Duration duration = TDToast._defaultDisPlayDuration}) { - _showOverlay(_TDIconTextToast(text: text, iconData: TDIcons.error_circle, iconTextDirection: direction,), context: context); - } - - static void showLoading({required BuildContext context, - String? text, - Duration duration = TDToast._defaultDisPlayDuration}) { - _showOverlay(_TDToastLoading(text: text,), context: context, duration: TDToast._infinteDuration); - } - - static void dismissLoading() { - _cancel(); - } - - static void _showOverlay(Widget? widget, {required BuildContext context, - Duration duration = TDToast._defaultDisPlayDuration}) { - _cancel(); - _showing = true; - var overlayState = Overlay.of(context); - _overlayEntry = OverlayEntry( - builder: (BuildContext context) => Center( - child: AnimatedOpacity( - opacity: _showing ? 1.0 : 0.0, - duration: _showing - ? const Duration(milliseconds: 100) - : const Duration(milliseconds: 200), - child: widget, - ), - )); - if (_overlayEntry != null) { - overlayState?.insert(_overlayEntry!); - } - _startTimer(duration); - } - - static void _cancel() { - _timer?.cancel(); - _timer = null; - _disposeTimer?.cancel(); - _disposeTimer = null; - _overlayEntry?.remove(); - _overlayEntry = null; - _showing = false; - } - - static void _startTimer(Duration duration) { - _timer?.cancel(); - _disposeTimer?.cancel(); - _timer = Timer(duration, () { - _showing = false; - _overlayEntry?.markNeedsBuild(); - _timer = null; - _disposeTimer = Timer(const Duration(milliseconds: 200), () { - _overlayEntry?.remove(); - _overlayEntry = null; - _disposeTimer = null; - }); - }); - } - - static OverlayEntry? _overlayEntry; - static bool _showing = false; - static Timer? _timer; - static Timer? _disposeTimer; - static const Duration _defaultDisPlayDuration = Duration(milliseconds: 3000); - static const Duration _infinteDuration = Duration(seconds: 99999999); -} - -class _TDIconTextToast extends StatelessWidget { - final String? text; - final IconData? iconData; - final IconTextDirection iconTextDirection; - const _TDIconTextToast({this.text, this.iconData, this.iconTextDirection = IconTextDirection.horizontal}); - - Widget buildHorizontalWidgets(BuildContext context) { - return ConstrainedBox(constraints: const BoxConstraints(maxWidth: 191, maxHeight: 94), child: Container( - padding: const EdgeInsets.fromLTRB(24, 14, 24, 14), - decoration: BoxDecoration( - color: TDTheme.of(context).fontGyColor2, - borderRadius: BorderRadius.circular(4), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(iconData, size: 21, color: TDTheme.of(context).whiteColor1,), - const SizedBox(width: 10,), - TDText(text ?? '', - font: TDTheme.of(context).fontS, - fontWeight: FontWeight.w400, - maxLines: 1, - overflow: TextOverflow.ellipsis, - textColor: TDTheme.of(context).whiteColor1,) - ],) - ),); - } - - Widget buildVerticalWidgets(BuildContext context) { - return ConstrainedBox(constraints: const BoxConstraints(maxWidth: 136, maxHeight: 130), child: Container( - width: 136, - height: 130, - padding: const EdgeInsets.fromLTRB(24, 24, 24, 24), - decoration: BoxDecoration( - color: TDTheme.of(context).fontGyColor2, - borderRadius: BorderRadius.circular(8), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Icon(iconData, size: 42, color: TDTheme.of(context).whiteColor1,), - const SizedBox(height: 15,), - TDText(text ?? '', - font: TDTheme.of(context).fontS, - fontWeight: FontWeight.w400, - maxLines: 1, - overflow: TextOverflow.ellipsis, - textColor: TDTheme.of(context).whiteColor1,) - ],) - )); - } - - @override - Widget build(BuildContext context) { - return iconTextDirection == IconTextDirection.horizontal ? buildHorizontalWidgets(context) : buildVerticalWidgets(context); - } -} - -class _TDToastLoading extends StatelessWidget { - final String? text; - const _TDToastLoading({this.text}); - - @override - Widget build(BuildContext context) { - return Container( - width: 136, - height: 130, - padding: const EdgeInsets.fromLTRB(24, 24, 24, 24), - decoration: BoxDecoration( - color: TDTheme.of(context).fontGyColor2, - borderRadius: BorderRadius.circular(8), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TDCircleIndicator(color: TDTheme.of(context).whiteColor1, size: 36, lineWidth: 6,), - const SizedBox(height: 15,), - TDText(text ?? '加载中...', - font: TDTheme.of(context).fontS, - fontWeight: FontWeight.w400, - maxLines: 1, - overflow: TextOverflow.ellipsis, - textColor: TDTheme.of(context).whiteColor1,) - ],) - ); - } -} - -class _TDTextToast extends StatelessWidget { - final String? text; - const _TDTextToast({this.text}); - - @override - Widget build(BuildContext context) { - return ConstrainedBox(constraints: const BoxConstraints(maxWidth: 191, maxHeight: 94), child: Container( - padding: const EdgeInsets.fromLTRB(24, 14, 24, 14), - decoration: BoxDecoration( - color: TDTheme.of(context).fontGyColor2, - borderRadius: BorderRadius.circular(4), - ), - child: TDText(text ?? '', - font: TDTheme.of(context).fontS, - fontWeight: FontWeight.w400, - maxLines: 3, - overflow: TextOverflow.ellipsis, - textColor: TDTheme.of(context).whiteColor1,) - ),); - } -} - diff --git a/lib/src/theme/basic.dart b/lib/src/theme/basic.dart deleted file mode 100644 index b41b0981a..000000000 --- a/lib/src/theme/basic.dart +++ /dev/null @@ -1,23 +0,0 @@ -/// 字体宽高数据 -class Font { - late double size; - late double height; - - Font({required int size, required int lineHeight}) { - this.size = size.toDouble(); - height = lineHeight.toDouble() / size; - } - - factory Font.fromJson(Map map) => - Font(size: map['size'], lineHeight: map['lineHeight']); -} - -/// 字体样式 -class FontFamily { - late String fontFamily; - - FontFamily({required this.fontFamily}); - - factory FontFamily.fromJson(Map map) => - FontFamily(fontFamily: map['fontFamily']); -} diff --git a/lib/src/theme/td_corners.dart b/lib/src/theme/td_corners.dart deleted file mode 100644 index 0efd67e94..000000000 --- a/lib/src/theme/td_corners.dart +++ /dev/null @@ -1,9 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'td_theme.dart'; - -/// 内置圆角数据 -extension TDCorners on TDThemeData { - /// 基础圆角数据 - double? get baseBorderRadius => cornerMap['baseBorderRadius']; -} diff --git a/lib/src/theme/td_default_theme.dart b/lib/src/theme/td_default_theme.dart deleted file mode 100644 index 968fd609e..000000000 --- a/lib/src/theme/td_default_theme.dart +++ /dev/null @@ -1,235 +0,0 @@ -/// TDesign默认主题 -class TDDefaultTheme { - static String defaultThemeConfig = ''' - { - "default": { - "color": { - "brandColor1": "#ECF2FE", - "brandColor2": "#D4E3FC", - "brandColor3": "#BBD3FB", - "brandColor4": "#96BBF8", - "brandColor5": "#699EF5", - "brandColor6": "#4787F0", - "brandColor7": "#266FE8", - "brandColor8": "#0052D9", - "brandColor9": "#0034B5", - "brandColor10": "#001F97", - "brandLightColor": "#ECF2FE", - "brandFocusColor": "#D4E3FC", - "brandDisabledColor": "#BBD3FB", - "brandHoverColor": "#266FE8", - "brandNormalColor": "#0052D9", - "brandClickColor": "#0034B5", - "errorColor1": "#FDECEE", - "errorColor2": "#F9D7D9", - "errorColor3": "#F8B9BE", - "errorColor4": "#F78D94", - "errorColor5": "#F36D78", - "errorColor6": "#E34D59", - "errorColor7": "#C9353F", - "errorColor8": "#B11F26", - "errorColor9": "#951114", - "errorColor10": "#680506", - "errorLightColor": "#FDECEE", - "errorFocusColor": "#F9D7D9", - "errorDisabledColor": "#F8B9BE", - "errorHoverColor": "#F36D78", - "errorNormalColor": "#E34D59", - "errorClickColor": "#C9353F", - "warningColor1": "#FEF3E6", - "warningColor2": "#F9E0C7", - "warningColor3": "#F7C797", - "warningColor4": "#F2995F", - "warningColor5": "#ED7B2F", - "warningColor6": "#D35A21", - "warningColor7": "#BA431B", - "warningColor8": "#9E3610", - "warningColor9": "#842B0B", - "warningColor10": "#5A1907", - "warningLightColor": "#FEF3E6", - "warningFocusColor": "#F9E0C7", - "warningDisabledColor": "#F7C797", - "warningHoverColor": "#F2995F", - "warningNormalColor": "#ED7B2F", - "warningClickColor": "#D35A21", - "successColor1": "#E8F8F2", - "successColor2": "#BCEBDC", - "successColor3": "#85DBBE", - "successColor4": "#48C79C", - "successColor5": "#00A870", - "successColor6": "#078D5C", - "successColor7": "#067945", - "successColor8": "#056334", - "successColor9": "#044F2A", - "successColor10": "#033017", - "successLightColor": "#E8F8F2", - "successFocusColor": "#BCEBDC", - "successDisabledColor": "#85DBBE", - "successHoverColor": "#48C79C", - "successNormalColor": "#00A870", - "successClickColor": "#078D5C", - "fontGyColor1": "#E6000000", - "fontGyColor2": "#99000000", - "fontGyColor3": "#66000000", - "fontGyColor4": "#42000000", - "fontWhColor1": "#FFFFFFFF", - "fontWhColor2": "#8CFFFFFF", - "fontWhColor3": "#59FFFFFF", - "fontWhColor4": "#38FFFFFF", - "whiteColor1": "#FFFFFF", - "grayColor1": "#F3F3F3", - "grayColor2": "#EEEEEE", - "grayColor3": "#E7E7E7", - "grayColor4": "#DCDCDC", - "grayColor5": "#C5C5C5", - "grayColor6": "#A6A6A6", - "grayColor7": "#8B8B8B", - "grayColor8": "#777777", - "grayColor9": "#5E5E5E", - "grayColor10": "#4B4B4B", - "grayColor11": "#383838", - "grayColor12": "#2C2C2C", - "grayColor13": "#242424", - "grayColor14": "#181818" - }, - "font": { - "fontXL": { - "size": 36, - "lineHeight": 44 - }, - "fontL": { - "size": 20, - "lineHeight": 28 - }, - "fontM": { - "size": 16, - "lineHeight": 24 - }, - "fontS": { - "size": 14, - "lineHeight": 22 - }, - "fontXS": { - "size": 12, - "lineHeight": 20 - }, - "fontXXS": { - "size": 10, - "lineHeight": 16 - } - }, - "fontFamily": { - "numberFontFamily": { - "fontFamily": "DINAlternate-B" - } - }, - "corner": { - "baseBorderRadius": 8 - }, - "shadow": { - "baseShadows": [ - { - "color": "#0D000000", - "blurRadius": 10, - "spreadRadius": 1, - "offset":{ - "x":0, - "y":1 - } - }, - { - "color": "#14000000", - "blurRadius": 5, - "spreadRadius": 1, - "offset":{ - "x":0, - "y":4 - } - }, - { - "color": "#1F000000", - "blurRadius": 4, - "spreadRadius": -1, - "offset":{ - "x":0, - "y":2 - } - } - ], - "middleShadows": [ - { - "color": "#0D000000", - "blurRadius": 14, - "spreadRadius": 2, - "offset":{ - "x":0, - "y":3 - } - }, - { - "color": "#0F000000", - "blurRadius": 10, - "spreadRadius": 1, - "offset":{ - "x":0, - "y":8 - } - }, - { - "color": "#1A000000", - "blurRadius": 5, - "spreadRadius": -3, - "offset":{ - "x":0, - "y":0 - } - } - ], - "topShadows": [ - { - "color": "#0D000000", - "blurRadius": 30, - "spreadRadius": 5, - "offset":{ - "x":0, - "y":6 - } - }, - { - "color": "#0A000000", - "blurRadius": 24, - "spreadRadius": 2, - "offset":{ - "x":0, - "y":16 - } - }, - { - "color": "#14000000", - "blurRadius": 10, - "spreadRadius": -5, - "offset":{ - "x":0, - "y":8 - } - } - ] - }, - "spacer": { - "spacer4": 4, - "spacer8": 8, - "spacer12": 12, - "spacer16": 16, - "spacer24": 24, - "spacer32": 32, - "spacer40": 40, - "spacer48": 48, - "spacer64": 64, - "spacer96": 96, - "spacer160": 160 - } - } -} - - '''; -} diff --git a/lib/src/theme/td_fonts.dart b/lib/src/theme/td_fonts.dart deleted file mode 100644 index 4d1799b41..000000000 --- a/lib/src/theme/td_fonts.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'basic.dart'; -import 'td_theme.dart'; - -/// 内置字体数据 -extension TDFonts on TDThemeData { - /// 字体大小/行高 - /// 36/44 - Font? get fontXL => fontMap['fontXL']; - - /// 20/28 - Font? get fontL => fontMap['fontL']; - - /// 16/24 - Font? get fontM => fontMap['fontM']; - - /// 14/22 - Font? get fontS => fontMap['fontS']; - - /// 12/20 - Font? get fontXS => fontMap['fontXS']; - - /// 10/16 - Font? get fontXXS => fontMap['fontXXS']; -} diff --git a/lib/src/theme/td_shadows.dart b/lib/src/theme/td_shadows.dart deleted file mode 100644 index dacf2c73a..000000000 --- a/lib/src/theme/td_shadows.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter/material.dart'; -import 'td_theme.dart'; - -/// 内置投影 -extension TDBoxShadows on TDThemeData { - /// 基础投影 - List? get baseShadows => shadowMap['baseShadows']; - - /// 中层投影 - List? get middleShadows => shadowMap['middleShadows']; - - /// 上层投影 - List? get topShadows => shadowMap['topShadows']; -} diff --git a/lib/src/theme/td_theme.dart b/lib/src/theme/td_theme.dart deleted file mode 100644 index cca070eac..000000000 --- a/lib/src/theme/td_theme.dart +++ /dev/null @@ -1,277 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/material.dart'; - -import '../util/log.dart'; -import '../util/string_util.dart'; -import 'basic.dart'; -import 'td_default_theme.dart'; - -/// 主题控件 -class TDTheme extends StatelessWidget { - - const TDTheme( - {required this.data, required this.child, this.systemData, Key? key}) - : super(key: key); - - final Widget child; - - final TDThemeData data; - - /// Flutter系统主题数据 - final ThemeData? systemData; - - @override - Widget build(BuildContext context) { - Widget result = _TDInheritedTheme( - theme: this, - child: child, - ); - if (systemData != null) { - result = Theme(data: systemData!, child: result); - } - return result; - } - - /// 获取默认主题数据,全局唯一 - static TDThemeData defaultData() { - return TDThemeData.defaultData(); - } - - /// 获取主题数据,如果未传context则获取全局唯一的默认数据, - /// 传了context,则获取最近的主题,取不到则会获取全局唯一默认数据 - static TDThemeData of([BuildContext? context]) { - if (context != null) { - // 如果传了context,则从其中获取最近主题 - final inheritedTheme = - context.dependOnInheritedWidgetOfExactType<_TDInheritedTheme>(); - return inheritedTheme?.theme.data ?? TDThemeData.defaultData(); - } else { - // 如果context为null,则返回全局默认主题 - return TDThemeData.defaultData(); - } - } -} - -/// 存储主题数据的内部控件 -class _TDInheritedTheme extends InheritedWidget { - final TDTheme theme; - - const _TDInheritedTheme( - {required this.theme, Key? key, required Widget child}) - : super(key: key, child: child); - - @override - bool updateShouldNotify(covariant _TDInheritedTheme oldWidget) { - return theme.data != oldWidget.theme.data; - } -} - -/// 主题数据 -class TDThemeData { - static const String _defaultThemeName = 'default'; - static TDThemeData? _defaultThemeData; - - late String name; - late Map colorMap; - late Map fontMap; - late Map cornerMap; - late Map fontFamilyMap; - late Map> shadowMap; - late Map spacerMap; - late TDExtraThemeData? extraThemeData; - - TDThemeData( - {required this.name, - required this.colorMap, - required this.fontMap, - required this.cornerMap, - required this.fontFamilyMap, - required this.shadowMap, - required this.spacerMap, - this.extraThemeData, - bool recoverDefault = false}); - - /// 获取默认Data,一个App里只有一个,用于没有context的地方 - static TDThemeData defaultData({TDExtraThemeData? extraThemeData}) { - _defaultThemeData ??= fromJson( - _defaultThemeName, TDDefaultTheme.defaultThemeConfig, - extraThemeData: extraThemeData) ?? - _emptyData(_defaultThemeName, extraThemeData: extraThemeData); - - return _defaultThemeData!; - } - - /// 从父类拷贝 - TDThemeData copyWith( - String name, { - Map? colorMap, - Map? fontMap, - Map? cornerMap, - Map? fontFamilyMap, - Map>? shadowMap, - Map? marginMap, - TDExtraThemeData? extraThemeData, - }) { - var result = TDThemeData( - name: name, - colorMap: _copyMap(this.colorMap, colorMap), - fontMap: _copyMap(this.fontMap, fontMap), - cornerMap: _copyMap(this.cornerMap, cornerMap), - fontFamilyMap: _copyMap(this.fontFamilyMap, fontFamilyMap), - shadowMap: _copyMap>(this.shadowMap, shadowMap), - spacerMap: _copyMap(spacerMap, marginMap), - extraThemeData: extraThemeData ?? this.extraThemeData); - - return result; - } - - /// 拷贝Map,防止内层 - Map _copyMap(Map src, Map? add) { - var map = {}; - src.forEach((key, value) { - map[key] = value; - }); - if (add != null) { - map.addAll(add); - } - return map; - } - - /// 创建空对象 - static TDThemeData _emptyData(String name, - {TDExtraThemeData? extraThemeData}) { - return TDThemeData( - name: name, - colorMap: {}, - fontMap: {}, - cornerMap: {}, - fontFamilyMap: {}, - shadowMap: {}, - spacerMap: {}); - } - - /// 解析配置的json文件为主题数据 - static TDThemeData? fromJson(String name, String themeJson, - {var recoverDefault = false, TDExtraThemeData? extraThemeData}) { - if (themeJson.isEmpty) { - Log.e('TTheme', 'parse themeJson is empty'); - return null; - } - try { - /// 要求json配置必须正确 - final themeConfig = json.decode(themeJson); - if (themeConfig.containsKey(name)) { - var theme = _emptyData(name); - Map curThemeMap = themeConfig['$name']; - - /// 设置颜色 - Map? colorsMap = curThemeMap['color']; - colorsMap?.forEach((key, value) { - theme.colorMap[key] = toColor(value); - }); - - /// 设置字体尺寸 - Map? fontsMap = curThemeMap['font']; - fontsMap?.forEach((key, value) { - theme.fontMap[key] = - Font(size: value['size'], lineHeight: value['lineHeight']); - }); - - /// 设置圆角 - Map? cornersMap = curThemeMap['corner']; - cornersMap?.forEach((key, value) { - theme.cornerMap[key] = value.toDouble(); - }); - - /// 设置字体 - Map? fontFamilyMap = curThemeMap['fontFamily']; - fontFamilyMap?.forEach((key, value) { - theme.fontFamilyMap[key] = FontFamily(fontFamily: value['fontFamily']); - }); - - /// 设置阴影 - Map? shadowMap = curThemeMap['shadow']; - shadowMap?.forEach((key, value) { - var list = []; - (value as List).forEach((element) { - list.add(BoxShadow( - color: toColor(element['color']), - blurRadius: element['blurRadius'].toDouble(), - spreadRadius: element['spreadRadius'].toDouble(), - offset: Offset(element['offset']?['x'].toDouble() ?? 0, - element['offset']?['y'].toDouble() ?? 0), - )); - }); - - theme.shadowMap[key] = list; - }); - - /// 设置Margin - Map? marginsMap = curThemeMap['margin']; - marginsMap?.forEach((key, value) { - theme.spacerMap[key] = value.toDouble(); - }); - - if (extraThemeData != null) { - extraThemeData.parse(name, curThemeMap); - theme.extraThemeData = extraThemeData; - } - if (recoverDefault) { - _defaultThemeData = theme; - } - return theme; - } else { - Log.e('TTheme', - 'load theme error ,not found the theme with name:${name}'); - return null; - } - } catch (e) { - Log.e('TTheme', 'parse theme data error:${e}'); - return null; - } - } - - Color? ofColor( - String? key, - ) { - return colorMap[key]; - } - - Font? ofFont(String? key) { - return fontMap[key]; - } - - double? ofCorner( - String? key, - ) { - return cornerMap[key]; - } - - FontFamily? ofFontFamily( - String? key, - ) { - return fontFamilyMap[key]; - } - - List? ofShadow( - String? key, - ) { - return shadowMap[key]; - } - - T? ofExtra() { - try { - return extraThemeData as T; - } catch (e) { - Log.e('TDThemeData ofExtra error: $e'); - } - return null; - } -} - -/// 扩展主题数据 -abstract class TDExtraThemeData { - /// 解析json - void parse(String name, Map curThemeMap); -} diff --git a/lib/src/util/string_util.dart b/lib/src/util/string_util.dart deleted file mode 100644 index 4df4fcbf1..000000000 --- a/lib/src/util/string_util.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/material.dart'; - -Color toColor(String colorStr, {double alpha = 1}) { - var hexColor = colorStr.toUpperCase().replaceAll('#', ''); - if (hexColor.length == 6) { - if (alpha < 0) { - alpha = 0; - } else if (alpha > 1) { - alpha = 1; - } - var alphaInt = (0xFF * alpha).toInt(); - var alphaString = alphaInt.toRadixString(16); - - hexColor = '$alphaString$hexColor'; - } - return Color(int.parse(hexColor, radix: 16)); -} diff --git a/lib/td_export.dart b/lib/td_export.dart deleted file mode 100644 index 97787d973..000000000 --- a/lib/td_export.dart +++ /dev/null @@ -1,43 +0,0 @@ -export 'src/components/avatar/td_avatar.dart'; -export 'src/components/badge/td_badge.dart'; -export 'src/components/bottom_nav_bar/td_bottom_nav_bar.dart'; -export 'src/components/button/td_button.dart'; -export 'src/components/button/td_button_style.dart'; -export 'src/components/checkbox/td_check_box.dart'; -export 'src/components/checkbox/td_check_box_group.dart'; -export 'src/components/dialog/td_dialog.dart'; -export 'src/components/divider/td_divider.dart'; -export 'src/components/empty/td_empty.dart'; -export 'src/components/icon/td_icons.dart'; -export 'src/components/image/image_widget.dart'; -export 'src/components/image/td_image.dart'; -export 'src/components/input/input_view.dart'; -export 'src/components/input/td_input.dart'; -export 'src/components/loading/td_loading.dart'; -export 'src/components/navbar/td_nav_bar.dart'; -export 'src/components/picker/td_date_picker.dart'; -export 'src/components/picker/td_item_widget.dart'; -export 'src/components/picker/td_multi_picker.dart'; -export 'src/components/picker/td_picker.dart'; -export 'src/components/popup/td_popup_route.dart'; -export 'src/components/radio/td_radio.dart'; -export 'src/components/refresh/td_refresh_header.dart'; -export 'src/components/search/td_search_bar.dart'; -export 'src/components/swiper/td_swiper.dart'; -export 'src/components/switch/td_switch.dart'; -export 'src/components/tabbar/td_tab.dart'; -export 'src/components/tabbar/td_tab_bar.dart'; -export 'src/components/tabbar/td_tab_bar_vertical_view.dart'; -export 'src/components/tabbar/td_tab_bar_view.dart'; -export 'src/components/tag/td_tag.dart'; -export 'src/components/tag/td_tag_styles.dart'; -export 'src/components/text/td_text.dart'; -export 'src/components/toast/td_toast.dart'; -export 'src/theme/basic.dart'; -export 'src/theme/td_colors.dart'; -export 'src/theme/td_corners.dart'; -export 'src/theme/td_font_family.dart'; -export 'src/theme/td_fonts.dart'; -export 'src/theme/td_shadows.dart'; -export 'src/theme/td_theme.dart'; -export 'src/util/platform_util.dart'; \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index b157022e8..000000000 --- a/pubspec.lock +++ /dev/null @@ -1,168 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.9.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.1" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.16.0" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.3.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_easyrefresh: - dependency: "direct main" - description: - name: flutter_easyrefresh - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.2.2" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.4" - flutter_swiper_null_safety: - dependency: "direct main" - description: - name: flutter_swiper_null_safety - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.2" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - lints: - dependency: transitive - description: - name: lints - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.0.1" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.12.12" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.1.5" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.8.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.8.2" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.9.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.1" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.flutter-io.cn" - source: hosted - version: "0.4.12" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.1.2" -sdks: - dart: ">=2.17.0-0 <3.0.0" - flutter: ">=1.24.0" diff --git a/pubspec.yaml b/pubspec.yaml deleted file mode 100644 index 6cb6452fe..000000000 --- a/pubspec.yaml +++ /dev/null @@ -1,70 +0,0 @@ -name: tdesign_flutter -description: A new Flutter plugin. -version: 0.0.1 -homepage: - -environment: - sdk: ">=2.12.0 <3.0.0" - flutter: ">=1.20.0" - -dependencies: - flutter: - sdk: flutter - - # 轮播图组件 - flutter_swiper_null_safety: ^1.0.2 - - # 下拉刷新组件 - flutter_easyrefresh: ^2.2.1 - -dev_dependencies: - flutter_lints: ^1.0.4 - flutter_test: - sdk: flutter - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. -flutter: - # This section identifies this Flutter project as a plugin project. - # The 'pluginClass' and Android 'package' identifiers should not ordinarily - # be modified. They are used by the tooling to maintain consistency when - # adding or updating assets for this project. - uses-material-design: true - fonts: - - family: TDIcons - fonts: - - asset: assets/tdesign/td_icons.ttf - - - # To add assets to your plugin package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # To add custom fonts to your plugin package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/tdesign-adaptation/.gitignore b/tdesign-adaptation/.gitignore new file mode 100644 index 000000000..ac5aa9893 --- /dev/null +++ b/tdesign-adaptation/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/tdesign-adaptation/.metadata b/tdesign-adaptation/.metadata new file mode 100644 index 000000000..17c60361b --- /dev/null +++ b/tdesign-adaptation/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "41456452f29d64e8deb623a3c927524bcf9f111b" + channel: "[user-branch]" + +project_type: package diff --git a/CHANGELOG.md b/tdesign-adaptation/CHANGELOG.md similarity index 100% rename from CHANGELOG.md rename to tdesign-adaptation/CHANGELOG.md diff --git a/tdesign-adaptation/LICENSE b/tdesign-adaptation/LICENSE new file mode 100644 index 000000000..018620472 --- /dev/null +++ b/tdesign-adaptation/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2023-present TDesign, tdesign@tencent.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/tdesign-adaptation/README.md b/tdesign-adaptation/README.md new file mode 100644 index 000000000..53d0fb7eb --- /dev/null +++ b/tdesign-adaptation/README.md @@ -0,0 +1,4 @@ +# tdesign_flutter_adaptation + +tdesign_flutter adaptation package. + diff --git a/tdesign-adaptation/analysis_options.yaml b/tdesign-adaptation/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/tdesign-adaptation/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:flutter_lints/flutter.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/tdesign-adaptation/lib/components/indexes/sticky_header/value_layout_builder.dart b/tdesign-adaptation/lib/components/indexes/sticky_header/value_layout_builder.dart new file mode 100644 index 000000000..029a05f65 --- /dev/null +++ b/tdesign-adaptation/lib/components/indexes/sticky_header/value_layout_builder.dart @@ -0,0 +1,130 @@ +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; + +/// The signature of the [ValueLayoutBuilder] builder function. +typedef ValueLayoutWidgetBuilder = Widget Function( + BuildContext context, + BoxValueConstraints constraints, + ); + +class BoxValueConstraints extends BoxConstraints { + BoxValueConstraints({ + required this.value, + required BoxConstraints constraints, + }) : super( + minWidth: constraints.minWidth, + maxWidth: constraints.maxWidth, + minHeight: constraints.minHeight, + maxHeight: constraints.maxHeight, + ); + + final T value; + + @override + bool operator ==(dynamic other) { + assert(debugAssertIsValid()); + if (identical(this, other)) { + return true; + } + if (other is! BoxValueConstraints) { + return false; + } + final typedOther = other; + assert(typedOther.debugAssertIsValid()); + return value == typedOther.value && + minWidth == typedOther.minWidth && + maxWidth == typedOther.maxWidth && + minHeight == typedOther.minHeight && + maxHeight == typedOther.maxHeight; + } + + @override + int get hashCode { + assert(debugAssertIsValid()); + return Object.hash(minWidth, maxWidth, minHeight, maxHeight, value); + } +} + +/// Builds a widget tree that can depend on the parent widget's size and a extra +/// value. +/// +/// Similar to the [LayoutBuilder] widget except that the constraints contains +/// an extra value. +/// +class ValueLayoutBuilder extends ConstrainedLayoutBuilder> { + /// Creates a widget that defers its building until layout. + const ValueLayoutBuilder({ + Key? key, + required ValueLayoutWidgetBuilder builder, + }) : super(key: key, builder: builder); + + @override + ValueLayoutWidgetBuilder get builder => super.builder; + + @override + _RenderValueLayoutBuilder createRenderObject(BuildContext context) => _RenderValueLayoutBuilder(); +} + +class _RenderValueLayoutBuilder extends RenderBox + with RenderObjectWithChildMixin, RenderObjectWithLayoutCallbackMixin, RenderAbstractLayoutBuilderMixin, RenderBox> { + @override + double computeMinIntrinsicWidth(double height) { + assert(_debugThrowIfNotCheckingIntrinsics()); + return 0.0; + } + + @override + double computeMaxIntrinsicWidth(double height) { + assert(_debugThrowIfNotCheckingIntrinsics()); + return 0.0; + } + + @override + double computeMinIntrinsicHeight(double width) { + assert(_debugThrowIfNotCheckingIntrinsics()); + return 0.0; + } + + @override + double computeMaxIntrinsicHeight(double width) { + assert(_debugThrowIfNotCheckingIntrinsics()); + return 0.0; + } + + @override + void performLayout() { + final constraints = this.constraints; + runLayoutCallback(); + if (child != null) { + child!.layout(constraints, parentUsesSize: true); + size = constraints.constrain(child!.size); + } else { + size = constraints.biggest; + } + } + + @override + bool hitTestChildren(BoxHitTestResult result, {required Offset position}) { + return child?.hitTest(result, position: position) ?? false; + } + + @override + void paint(PaintingContext context, Offset offset) { + if (child != null) { + context.paintChild(child!, offset); + } + } + + bool _debugThrowIfNotCheckingIntrinsics() { + assert(() { + if (!RenderObject.debugCheckingIntrinsics) { + throw FlutterError('ValueLayoutBuilder does not support returning intrinsic dimensions.\n' + 'Calculating the intrinsic dimensions would require running the layout ' + 'callback speculatively, which might mutate the live render object tree.'); + } + return true; + }()); + + return true; + } +} diff --git a/tdesign-adaptation/lib/tdesign_adaptation.dart b/tdesign-adaptation/lib/tdesign_adaptation.dart new file mode 100644 index 000000000..148b93467 --- /dev/null +++ b/tdesign-adaptation/lib/tdesign_adaptation.dart @@ -0,0 +1,7 @@ +library tdesign_adaptation; + +/// A Calculator. +class Calculator { + /// Returns [value] plus 1. + int addOne(int value) => value + 1; +} diff --git a/tdesign-adaptation/pubspec.yaml b/tdesign-adaptation/pubspec.yaml new file mode 100644 index 000000000..78605176f --- /dev/null +++ b/tdesign-adaptation/pubspec.yaml @@ -0,0 +1,58 @@ +name: tdesign_flutter_adaptation +description: "tdesign_flutter adaptation package" +version: 3.32.0 +homepage: https://github.com/Tencent/tdesign-flutter + +environment: + sdk: '>=3.2.6 <4.0.0' + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + # 相册选择组件 + image_picker: 1.1.2 + image_picker_android: 0.8.12+23 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/tdesign-adaptation/test/tdesign_adaptation_test.dart b/tdesign-adaptation/test/tdesign_adaptation_test.dart new file mode 100644 index 000000000..04ceb1302 --- /dev/null +++ b/tdesign-adaptation/test/tdesign_adaptation_test.dart @@ -0,0 +1,12 @@ +import 'package:flutter_test/flutter_test.dart'; + +import 'package:tdesign_flutter_adaptation/tdesign_adaptation.dart'; + +void main() { + test('adds one to input values', () { + final calculator = Calculator(); + expect(calculator.addOne(2), 3); + expect(calculator.addOne(-7), -6); + expect(calculator.addOne(0), 1); + }); +} diff --git a/tdesign-component/CHANGELOG.md b/tdesign-component/CHANGELOG.md new file mode 100644 index 000000000..ab01634a0 --- /dev/null +++ b/tdesign-component/CHANGELOG.md @@ -0,0 +1,416 @@ +## 🌈 0.2.3 `2025-07-09` +### 🚀 Features +- `TDPicker`: Supports prioritizing the retention of cascaded option values when switching @epoll-j ([#666](https://github.com/Tencent/tdesign-flutter/pull/666)) +- `TDTable`: Supports default row selection @ccXxx1aoBai ([#665](https://github.com/Tencent/tdesign-flutter/pull/665)) +- `TDCalendar`: Adds custom date cell functionality @epoll-j ([#667](https://github.com/Tencent/tdesign-flutter/pull/667)) +- `TDForm`: Adds Form component @shizhe2018 @SimonWuZY ([#620](https://github.com/Tencent/tdesign-flutter/pull/620)) +- `TDTable`: Separates TDTableCol attribute configuration and empty data configuration @ccXxx1aoBai ([#665](https://github.com/Tencent/tdesign-flutter/pull/665)) + +### 🐞 Bug Fixes +- `TDTable`: Fixes the issue with unselected icon display in table headers and the selection state problem under disabled conditions @ccXxx1aoBai ([#665](https://github.com/Tencent/tdesign-flutter/pull/665)) +- `TDTable`: Fixes the empty data issue in tables @ccXxx1aoBai ([#671](https://github.com/Tencent/tdesign-flutter/pull/671)) +- `TDDialog`: Fixes the issue where dialogs block the keyboard @jflin19990707 ([#669](https://github.com/Tencent/tdesign-flutter/pull/669)) +- `TDCollapse`: Updates the demo page name for collapse @jflin19990707 ([#670](https://github.com/Tencent/tdesign-flutter/pull/670)) +- `TDDropdownMenu`: Fixes the incorrect popup position calculation in nested routing scenarios @hcanyz ([#648](https://github.com/Tencent/tdesign-flutter/pull/648)) + + +## 🌈 0.2.2 `2025-06-13` +### 🚀 Features +- `TDTable`: Added support for row selection and custom row height. @ccXxx1aoBai ([#594](https://github.com/Tencent/tdesign-flutter/pull/594)) +- `TDTreeSelect`: Added partial multi-selection support. @epoll-j ([#587](https://github.com/Tencent/tdesign-flutter/pull/587)) +- `TDCell`: Added support for custom height and bottom divider. @ccXxx1aoBai ([#611](https://github.com/Tencent/tdesign-flutter/pull/611)) +- `TDNoticeBar`: Added support for custom number of text lines. @ccXxx1aoBai ([#611](https://github.com/Tencent/tdesign-flutter/pull/611)) +- `TDBottomTabBar`: Made onTap in TDButtonBottomTabBar support repeated clicks. @epoll-j @RSS1102 ([#586](https://github.com/Tencent/tdesign-flutter/pull/586)) +- `TDBottomTabBar`: Implemented tap ripple effects. @RSS1102 ([#626](https://github.com/Tencent/tdesign-flutter/pull/626)) +- `TDAvatar`: Added custom BoxFit parameter. @shizhe2018 ([#633](https://github.com/Tencent/tdesign-flutter/pull/633)) + +### 🐞 Bug Fixes +- `TDDatePicker`: Fixed minute-level time display issue and optimized hour/minute/second range calculation logic. @epoll-j ([#585](https://github.com/Tencent/tdesign-flutter/pull/585)) +- `TDSearchBar`: Added onTapOutside callback support. @cyjaysong ([#608](https://github.com/Tencent/tdesign-flutter/pull/608)) +- `TDDropdownMenu`: Added support for modifying selected icon color. @jflin19990707 ([#631](https://github.com/Tencent/tdesign-flutter/pull/631)) +- `TDTabBar`: Fixed text-icon conflict in TDBottomTabBarBasicType.iconText mode. @jflin19990707 ([#628](https://github.com/Tencent/tdesign-flutter/pull/628)) +- `TDEmpty`: Added custom styling support for action buttons. @jflin19990707 ([#624](https://github.com/Tencent/tdesign-flutter/pull/624)) +- `TDToast`: Added custom text support for toast. @jflin19990707 ([#625](https://github.com/Tencent/tdesign-flutter/pull/625)) +- `TDPopup`: Modified _measureChildHeight method to fix inability to adjust popup height via child component. @Jzow ([#591](https://github.com/Tencent/tdesign-flutter/pull/591)) +- `TDCascader`: Fixed empty state handling for query data. @shizhe2018 ([#635](https://github.com/Tencent/tdesign-flutter/pull/635)) + +### 🚧 Others + +- Adapted for Flutter 3.32 version. @Luozf12345 ([#636](https://github.com/Tencent/tdesign-flutter/pull/636)) + + +## 🌈 0.2.0 `2025-05-07` +### 🚀 Features +- `TDCellGroup`: Added `titleBackgroundColor` property for cell group title background color. @runoob-coder ([#539](https://github.com/Tencent/tdesign-flutter/pull/539)) +- `TDLink`: Replaced link parameter `LinkObj` with `MessageLink`, adjusted `TDLink` styles, and added click callback. @runoob-coder ([#554](https://github.com/Tencent/tdesign-flutter/pull/554)) +- `TDBottomTabBar`: Added custom title support for step bar component. @RSS1102 ([#576](https://github.com/Tencent/tdesign-flutter/pull/576)) +- `TDSlider`: Added slider tap event `onTap`. @RSS1102 ([#527](https://github.com/Tencent/tdesign-flutter/pull/527)) +- `TDCascader`: Added "Confirm" button at top-right corner to support selecting any option. @Luozf12345 +- `ImageViewer`: Added single image deletion support. @ccXxx1aoBai ([#581](https://github.com/Tencent/tdesign-flutter/pull/581)) +- `TDPopup`: Added custom size properties for popup title, left text, right text, and close button. @Jzow ([#582](https://github.com/Tencent/tdesign-flutter/pull/582)) +- `TDBottomTabBarTabConfig`: Added `onLongPress` event triggered by long-pressing tabs. @RSS1102 ([#580](https://github.com/Tencent/tdesign-flutter/pull/580)) + +### 🐞 Bug Fixes +- `TDFooter`: Fixed content overflow issue in link mode. @runoob-coder ([#554](https://github.com/Tencent/tdesign-flutter/pull/554)) +- `TDUpload`: Fixed file size limit error. @epoll-j ([#544](https://github.com/Tencent/tdesign-flutter/pull/544)) +- `TDImageViewer`: Added Swiper component property passthrough, click events, and style properties; supports custom buttons. @ccXxx1aoBai ([#561](https://github.com/Tencent/tdesign-flutter/pull/561)) +- `TDSlider`: Fixed edge drag failure and value/scale display issues in capsule type with range. @qfish ([#567](https://github.com/Tencent/tdesign-flutter/pull/567)) +- `TDInput`: Fixed width calculation defect for non-Chinese label input fields. @Jzow ([#564](https://github.com/Tencent/tdesign-flutter/pull/564)) +- `TDPopup`: Fixed inability to modify popup height via `height` in child component. @Jzow ([#571](https://github.com/Tencent/tdesign-flutter/pull/571)) +- `TDDropdownMenu`: Fixed single-select failure in specific scenarios. @1jialong ([#575](https://github.com/Tencent/tdesign-flutter/pull/575)) +- `TDToast`: Fixed multi-line text display issue. @Luozf12345 +- `TDPopup`: Fixed horizontal line display issue when popup lacks outer Scaffold. @Luozf12345 + +### 🚧 Others +- `TDFooter`: Refactored `TDFooter` component; Removed `LinkObj` class and directly used `TDLink` class; Removed `isWithUnderline` parameter (link styles now set in `TDLink`). @runoob-coder ([#554](https://github.com/Tencent/tdesign-flutter/pull/554)) + + + +## 🌈 0.1.9 `2025-03-31` +### 🚀 Features +- `TDProgress`: Added `Progress` component @CORCTON ([#307](https://github.com/Tencent/tdesign-flutter/pull/307)) +- `TDMessage`: Added `Message` (Global Prompt) component @chendingya ([#316](https://github.com/Tencent/tdesign-flutter/pull/316)) +- `TDSkeleton`: Added `Skeleton` component @Ezer015 ([#317](https://github.com/Tencent/tdesign-flutter/pull/317)) +- `TDFooter`: Added `Footer` component @chendingya ([#224](https://github.com/Tencent/tdesign-flutter/pull/224)) +- `TDPopover`: Added `Popover` (Popup Bubble) component @ccXxx1aoBai ([#435](https://github.com/Tencent/tdesign-flutter/pull/435)) +- `TDSwitch`: Added custom "ON/OFF" font size support @shinyina ([#217](https://github.com/Tencent/tdesign-flutter/pull/217)) +- `TDDatePicker`: Added `filterItems` parameter for custom display options and `itemBuilder` for custom item rendering @hkaikai ([#426](https://github.com/Tencent/tdesign-flutter/pull/426)) +- `TDDrawer`: Created `TDDrawerWidget` component compatible with Scaffold's drawer property @hkaikai ([#445](https://github.com/Tencent/tdesign-flutter/pull/445)) +- `TDTable`: Custom columns now return current row number @ccXxx1aoBai ([#457](https://github.com/Tencent/tdesign-flutter/pull/457)) +- `TDUpload`: Added width/height settings and quick replacement configuration support @HubuHito ([#462](https://github.com/Tencent/tdesign-flutter/pull/462)) +- `TDButton`: Added icon position property @epoll-j ([#463](https://github.com/Tencent/tdesign-flutter/pull/463)) +- `TDDropdownMenu`: Supported single-select mode (`multiple == false`) with multi-column display (`optionsColumns > 1`) @hkaikai ([#502](https://github.com/Tencent/tdesign-flutter/pull/502)) +- `TDActionSheet`: Added Action Sheet component @hkaikai ([#485](https://github.com/Tencent/tdesign-flutter/pull/485)) +- `TDPicker`: Added `customSelectWidget` parameter @epoll-j ([#495](https://github.com/Tencent/tdesign-flutter/pull/495)) +- `TDSlider`: Added track color modification parameter @epoll-j ([#506](https://github.com/Tencent/tdesign-flutter/pull/506)) +- `TDCalendar`: Added animated scrolling to selected value position @hkaikai ([#509](https://github.com/Tencent/tdesign-flutter/pull/509)) +- `TDStep`: Added `CustomContent` parameter for Step content customization @Jzow ([#452](https://github.com/Tencent/tdesign-flutter/pull/452)) +- `TDTag`: Added `fixedWidth` parameter and fixed `TextOverflow.ellipsis` title overflow issue @Jzow ([#496](https://github.com/Tencent/tdesign-flutter/pull/496)) +- `TDPopup`: Added edge drag control for bottom panel @Jzow ([#514](https://github.com/Tencent/tdesign-flutter/pull/514)) +- `TDBadge`: Added capped numeric value setting for Badge @chendingya ([#302](https://github.com/Tencent/tdesign-flutter/pull/302)) +- `TDToast`: Added multi-line text support for icon-type toasts @ccXxx1aoBai ([#481](https://github.com/Tencent/tdesign-flutter/pull/481)) + +### 🐞 Bug Fixes +- `TDRefreshHeader`: Upgraded Easy Refresh to latest version, improved compatibility between v2/v3 syntax @hkaikai ([#438](https://github.com/Tencent/tdesign-flutter/pull/438)) +- `TDCell`: Fixed unresponsive click in blank area without default style; Improved default style construction and demo usage @hkaikai ([#448](https://github.com/Tencent/tdesign-flutter/pull/448)) +- `TDTable`: Fixed empty data image display issue @ccXxx1aoBai ([#451](https://github.com/Tencent/tdesign-flutter/pull/451)) +- `TDTabBar`: Added `labelStyle` and `unselectedLabelStyle` support for custom font sizes @hkaikai ([#453](https://github.com/Tencent/tdesign-flutter/pull/453)) +- `TDCalendar`: Fixed positioning issue when scrolling to last month @hkaikai ([#449](https://github.com/Tencent/tdesign-flutter/pull/449)) +- `TDBottomTabBar`: Fixed background color setting for capsule type @epoll-j ([#497](https://github.com/Tencent/tdesign-flutter/pull/497)) +- `TDCalendar`: Added localization for confirm button @hkaikai ([#505](https://github.com/Tencent/tdesign-flutter/pull/505)) +- `TDUpload`: Added `onMaxLimitReached` callback to handle file limit overflow @Jzow ([#474](https://github.com/Tencent/tdesign-flutter/pull/474)) +- `TDInput`: Added `_getTextWidth` function and click event to fix incomplete text display @Jzow ([#475](https://github.com/Tencent/tdesign-flutter/pull/475)) +- `TDImage`: Removed mandatory custom width/height constraints (default 72px) for layout auto-calculation @Jzow ([#499](https://github.com/Tencent/tdesign-flutter/pull/499)) +- `TDConfirmDialog`: Added layout constraints and dynamic max-height calculation with scroll support @Jzow ([#510](https://github.com/Tencent/tdesign-flutter/pull/510)) +- `TDDrawer`: Added `_deleteRouter()` call in close function to force clear routes @Jzow ([#512](https://github.com/Tencent/tdesign-flutter/pull/512)) +- `TDText`: Fixed text alignment issue in HarmonyOS 3.22 @duleigiser ([#437](https://github.com/Tencent/tdesign-flutter/pull/437)) +- `TDAlertDialog`: Fixed button style not filling width @lvjs ([#460](https://github.com/Tencent/tdesign-flutter/pull/460)) + +### 🚧 Others +- `TDSlider`: Demo code splitting @iamitis ([#245](https://github.com/Tencent/tdesign-flutter/pull/245)) +- Added release date to "About Us" page @iamitis ([#304](https://github.com/Tencent/tdesign-flutter/pull/304)) +- `Doc`: Updated README English version, added License file and Issue Doc templates @Jzow ([#458](https://github.com/Tencent/tdesign-flutter/pull/458)) + + + +## 🌈 0.1.8 `2024-12-30` +### 🚀 Features +- `TDUpload`: Added Upload component @TingShine ([#405](https://github.com/Tencent/tdesign-flutter/pull/405)) +- `SearchBar`: Added keyboard action type @ccXxx1aoBai ([#366](https://github.com/Tencent/tdesign-flutter/pull/366)) +- `Cell`: CellGroup added style control parameters: cardBorderRadius (card mode border radius), cardPadding (card mode padding), titlePadding (title padding) @hkaikai ([#409](https://github.com/Tencent/tdesign-flutter/pull/409)) +- `DropdownMenu`: Added decorator configuration: decoration, which can customize menu color and border @hkaikai ([#408](https://github.com/Tencent/tdesign-flutter/pull/408)) +- `ImageViewer`: Supports displaying image titles @ccXxx1aoBai ([#411](https://github.com/Tencent/tdesign-flutter/pull/411)) +- `Calendar`: Added monthTitleBuilder parameter @hkaikai ([#419](https://github.com/Tencent/tdesign-flutter/pull/419)) +- `Calendar`: Added pickerHeight, pickerItemCount parameters to control the height of the time selection component @hkaikai ([#421](https://github.com/Tencent/tdesign-flutter/pull/421)) +- `Toast`: Supports customizing the overlay background color @ccXxx1aoBai ([#423](https://github.com/Tencent/tdesign-flutter/pull/423)) +- `Rate`: Supports disabled parameter @hkaikai ([#357](https://github.com/Tencent/tdesign-flutter/pull/357)) +- `Calendar`: Modified CalendarBuilder return value to Widget @Luozf12345 ([#396](https://github.com/Tencent/tdesign-flutter/pull/396)) +- `SearchBar`: Added read-only attribute and click event @shizhe2018 ([#393](https://github.com/Tencent/tdesign-flutter/pull/393)) +- `Dialog`: TDDialogButtonOptions added font size attribute @shizhe2018 ([#381](https://github.com/Tencent/tdesign-flutter/pull/381)) +- `DateTimePicker`: Added time unit display attribute @shizhe2018 ([#383](https://github.com/Tencent/tdesign-flutter/pull/383)) +- `Input`: Added additionInfo left and right display position @shizhe2018 ([#401](https://github.com/Tencent/tdesign-flutter/pull/401)) + +### 🐞 Bug Fixes +- `NoticeBar`: Fixed the issue of abnormal text display on the web @ccXxx1aoBai ([#351](https://github.com/Tencent/tdesign-flutter/pull/351)) +- `Rate`: Fixed the issue where the onChange event was not triggered when clicking the tooltip in half selection @hkaikai ([#361](https://github.com/Tencent/tdesign-flutter/pull/361)) +- `Calendar`: Fixed the issue of inaccurate scroll position due to inconsistent number of rows in the month date @hkaikai ([#363](https://github.com/Tencent/tdesign-flutter/pull/363)) +- `Calendar`: Optimized the issue of rendering lag caused by too large min and max @hkaikai ([#363](https://github.com/Tencent/tdesign-flutter/pull/363)) +- `Input`: Fixed the issue where the dividing line and content were not aligned when setting contentPadding @epoll-j ([#365](https://github.com/Tencent/tdesign-flutter/pull/365)) +- `Table`: Fixed the issue of overflow when setting the width of fixed columns @ccXxx1aoBai ([#370](https://github.com/Tencent/tdesign-flutter/pull/370)) +- `Popup`: Fixed the issue of delay in closing when clicking on the overlay @hkaikai ([#380](https://github.com/Tencent/tdesign-flutter/pull/380)) +- `Cascader`: Added the function of clicking to select the first layer @shizhe2018 ([#355](https://github.com/Tencent/tdesign-flutter/pull/355)) +- `DateTimePicker`: Added restrictions on hours, minutes, and seconds @shizhe2018 ([#362](https://github.com/Tencent/tdesign-flutter/pull/362)) +- `Textarea`: Optimized the update of word limit changes @shizhe2018 ([#385](https://github.com/Tencent/tdesign-flutter/pull/385)) +- `TabBar`: Fixed the issue where labelStyle and unselectedLabelStyle did not take effect @shizhe2018 ([#399](https://github.com/Tencent/tdesign-flutter/pull/399)) +- `Picker`: Fixed the issue of unable to select color when sliding in multi-layer pop-up @shizhe2018 ([#413](https://github.com/Tencent/tdesign-flutter/pull/413)) +- `SearchBar`: Fixed the issue of SearchBar jittering at the default position when focusing, and the cursor not being centered @Luozf12345 ([#417](https://github.com/Tencent/tdesign-flutter/pull/417)) +- `Dialog`: Modified Dialog to only pass contentWidget, no need to pass title and content @Luozf12345 ([#418](https://github.com/Tencent/tdesign-flutter/pull/418)) +- `TDBottomTabBar`: Fixed the issue of bottom overflow by 2.5 pixels in iconText mode @epoll-j ([#422](https://github.com/Tencent/tdesign-flutter/pull/422)) + +### 🚧 Others +- Adapted to FlutterSdk3.25, the minimum supported version has been adjusted to 3.16.0 @shizhe2018 ([#378](https://github.com/Tencent/tdesign-flutter/pull/378)) +- Modified Example English version copy @shizhe2018 ([#382](https://github.com/Tencent/tdesign-flutter/pull/382)) +- Upgraded flutter_slidable version @Luozf12345 ([#407](https://github.com/Tencent/tdesign-flutter/pull/407)) +- Added component search function to demo @Luozf12345 ([#410](https://github.com/Tencent/tdesign-flutter/pull/410)) +- Updated Icons @Luozf12345 ([#420](https://github.com/Tencent/tdesign-flutter/pull/420)) + +## 🌈 0.1.7 `2024-10-16` +### 🚀 Features +- `TDNoticeBar`: Added noticeBar component @ccXxx1aoBai ([#162](https://github.com/Tencent/tdesign-flutter/pull/162)) +- `Result`: Added Result component @shinyina ([#220](https://github.com/Tencent/tdesign-flutter/pull/220)) +- `TimeCounter`: Timer component supports time display beyond conversion units, original TDCountDown component renamed to TimeCounter @hkaikai ([#272](https://github.com/Tencent/tdesign-flutter/pull/272)) +- `Calendar`: Added Calendar component @hkaikai ([#271](https://github.com/Tencent/tdesign-flutter/pull/271)) +- `Indexes`: Added Indexes component @hkaikai ([#321](https://github.com/Tencent/tdesign-flutter/pull/321)) +- `Table`: Added table component @ccXxx1aoBai ([#244](https://github.com/Tencent/tdesign-flutter/pull/244)) +- `Rate`: Added Rate component @hkaikai ([#338](https://github.com/Tencent/tdesign-flutter/pull/338)) +- `Dialog`: Supports custom content padding and buttons @ccXxx1aoBai ([#289](https://github.com/Tencent/tdesign-flutter/pull/289)) +- `Drawer`: Supports controlling the visibility of the divider, custom drawer background color, and controlling the display of the last divider @ccXxx1aoBai ([#278](https://github.com/Tencent/tdesign-flutter/pull/278)) +- `DropdownMenu`: Added control parameters for icon/width/height/icon and text alignment @hkaikai ([#297](https://github.com/Tencent/tdesign-flutter/pull/297)) +- `Search`: Added action and onActionClick properties @Ezer015 ([#263](https://github.com/Tencent/tdesign-flutter/pull/263)) +- `Avatar`: Added onTap event @ccXxx1aoBai ([#344](https://github.com/Tencent/tdesign-flutter/pull/344)) +- `TDDropdownMenu`: Added tabBarFlex parameter to TDDropdownItem to control width ratio @hkaikai ([#338](https://github.com/Tencent/tdesign-flutter/pull/338)) +- `SearchBar`: Feature/td searchbarfix added cursor height property @shizhe2018 ([#337](https://github.com/Tencent/tdesign-flutter/pull/337)) +- `TimeCounter`: Added forward timing function @epoll-j ([#246](https://github.com/Tencent/tdesign-flutter/pull/246)) +- `NavBar`: [NavBar] supports setting bottom shadow @ccXxx1aoBai ([#284](https://github.com/Tencent/tdesign-flutter/pull/284)) +- `Cell`: Added custom padding parameter @epoll-j ([#276](https://github.com/Tencent/tdesign-flutter/pull/276)) +- `Input`: Added onTapOutside callback @epoll-j ([#280](https://github.com/Tencent/tdesign-flutter/pull/280)) +- `Picker`: Added custom leftText, rightText @epoll-j ([#301](https://github.com/Tencent/tdesign-flutter/pull/301)) +- `Slider`: Feature/tdslider added text wrapping function @shizhe2018 ([#329](https://github.com/Tencent/tdesign-flutter/pull/329)) +- `Radio`: Feature/tdRadioGroup added built-in line wrapping, set number of rows and columns @shizhe2018 ([#331](https://github.com/Tencent/tdesign-flutter/pull/331)) +- `Dialog`: Added custom input box @shizhe2018 ([#333](https://github.com/Tencent/tdesign-flutter/pull/333)) +- `TDNavBar`: Added flexibleSpace parameter @Luozf12345 ([#341](https://github.com/Tencent/tdesign-flutter/pull/341)) +- `TDSearch`: Added search box focus acquisition and clear events @Luozf12345 ([#342](https://github.com/Tencent/tdesign-flutter/pull/342)) + +### 🐞 Bug Fixes +- `ImageViewer`: Fixed defaultIndex invalid issue @ccXxx1aoBai ([#292](https://github.com/Tencent/tdesign-flutter/pull/292)) +- `TimeCounter`: Fixed issue where it could not be reset repeatedly @hkaikai ([#272](https://github.com/Tencent/tdesign-flutter/pull/272)) +- `DropdownMenu`: Adjusted popup layer logic, fixed issue where back button could not be listened to @hkaikai ([#297](https://github.com/Tencent/tdesign-flutter/pull/297)) +- `DatePicker`: Removed monitoring on year, month, and day when destroyed to avoid memory leaks; added onSelectedItemChanged event @hkaikai ([#300](https://github.com/Tencent/tdesign-flutter/pull/300)) +- `SideBar`: Fixed issue where custom selected style text was not centered @ccXxx1aoBai ([#313](https://github.com/Tencent/tdesign-flutter/pull/313)) +- `Popup`: Fixed issue where multiple returns occurred when quickly clicking the mask @ccXxx1aoBai ([#318](https://github.com/Tencent/tdesign-flutter/pull/318)) +- `ImageViewer`: Fixed issue where deleting the first image caused display anomalies @ccXxx1aoBai ([#322](https://github.com/Tencent/tdesign-flutter/pull/322)) +- `SideBar`: Fixed issue where delayed loading components caused anchor point function anomalies @ccXxx1aoBai ([#343](https://github.com/Tencent/tdesign-flutter/pull/343)) +- `TDDropdownMenu`: Optimized menu display text to show ellipsis when exceeding display limit @hkaikai ([#338](https://github.com/Tencent/tdesign-flutter/pull/338)) +- `NoticeBar`: Fixed issue where it could not follow the theme color @ccXxx1aoBai ([#350](https://github.com/Tencent/tdesign-flutter/pull/350)) +- `Button`: Fixed overflow issue when setting shape to square or circle @epoll-j ([#257](https://github.com/Tencent/tdesign-flutter/pull/257)) +- `Slider`: Bugfix: Fixed issue where tb_slider setState did not update @arvinwli ([#298](https://github.com/Tencent/tdesign-flutter/pull/298)) +- `Cascader`: Fixed list sorting issue @shizhe2018 ([#303](https://github.com/Tencent/tdesign-flutter/pull/303)) +- `Popup`: Fixed issue where the keyboard would cover the input box in the Popup @epoll-j ([#264](https://github.com/Tencent/tdesign-flutter/pull/264)) +- `Cascader`: Fixed linkage time limit range logic @shizhe2018 ([#242](https://github.com/Tencent/tdesign-flutter/pull/242)) +- `Loading`: Fixed issue where dismissing Loading immediately after showing did not take effect @Luozf12345 ([#340](https://github.com/Tencent/tdesign-flutter/pull/340)) + +### 🚧 Others +- fix: remove useless output. @Ives7 ([#311](https://github.com/Tencent/tdesign-flutter/pull/311)) + + + +## 🌈 0.1.6 `2024-07-24` + +### 🚀 Features +- `Cell`: Added Cell component @hkaikai ([#150](https://github.com/Tencent/tdesign-flutter/pull/150)) +- `Drawer`: Added Drawer component @hkaikai ([#178](https://github.com/Tencent/tdesign-flutter/pull/178)) +- `SwipeCell`: Added SwipeCell component @hkaikai ([#218](https://github.com/Tencent/tdesign-flutter/pull/218)) +- `Steps`: Added Steps component @aaronmhl ([#199](https://github.com/Tencent/tdesign-flutter/pull/199)) +- `ImageViewer`: Added ImageViewer component @ccXxx1aoBai ([#187](https://github.com/Tencent/tdesign-flutter/pull/187)) +- `Cascader`: Added Cascader component @shizhe2018 ([#195](https://github.com/Tencent/tdesign-flutter/pull/195)) +- `Fab`: Added Fab component @TingShine ([#239](https://github.com/Tencent/tdesign-flutter/pull/239)) +- `BackTop`: Added BackTop component @TingShine ([#239](https://github.com/Tencent/tdesign-flutter/pull/239)) +- `TreeSelect`: Added TreeSelect component @TingShine ([#239](https://github.com/Tencent/tdesign-flutter/pull/239)) +- `Collapse`: Added Collapse component @dorayx ([#239](https://github.com/Tencent/tdesign-flutter/pull/239)) +- `Input`: Added inputAction API to support setting keyboard actions; added spacer API to customize component spacing @ccXxx1aoBai ([#184](https://github.com/Tencent/tdesign-flutter/pull/184)) +- `Text`: Added global font configuration and the ability to load web fonts @Luozf12345 ([#232](https://github.com/Tencent/tdesign-flutter/pull/232)) +- `CountDown`: Added start/reset/pause/resume control functions @hkaikai ([#175](https://github.com/Tencent/tdesign-flutter/pull/175)) +- `Popup`: Supported position and size settings @hkaikai ([#191](https://github.com/Tencent/tdesign-flutter/pull/191)) + +### 🐞 Bug Fixes +- `Toast`: Fixed the issue where the duration attribute was ineffective @ccXxx1aoBai ([#167](https://github.com/Tencent/tdesign-flutter/pull/167)) +- `Input`: Fixed the label overflow issue @ccXxx1aoBai ([#184](https://github.com/Tencent/tdesign-flutter/pull/184)) +- `Tabs`: For the tabs component, outlineType 'capsule' now supports setting selected and unselected tab background colors, and outlineType 'card' supports setting the selected tab background color @ccXxx1aoBai +- `Button`: Fixed the issue where properties could not be changed under the setState method @shizhe2018 ([#201](https://github.com/Tencent/tdesign-flutter/pull/201)) +- `SearchBar`: Added a controller to the search bar, allowing external clearing of search text @shizhe2018 ([#194](https://github.com/Tencent/tdesign-flutter/pull/194)) +- `Slider`: Added custom Decoration styles @shizhe2018 ([#198](https://github.com/Tencent/tdesign-flutter/pull/198)) +- `Empty`: Added text size style API @shizhe2018 ([#219](https://github.com/Tencent/tdesign-flutter/pull/219)) +- `Dialog`: Added input type background @shizhe2018 ([#238](https://github.com/Tencent/tdesign-flutter/pull/238)) + +### 🚧 Others +- HarmonyOS compilation support @hkaikai ([#233](https://github.com/Tencent/tdesign-flutter/pull/233)) +- Modified theme adaptation tool @Luozf12345 +- Added GitHub links for complete pages in demo code @Luozf12345 + + + +## 🌈 0.1.5 `2024-05-31` + +### 🚀 Features +- `TDDropdownMenu`: + - add: Added TDDropdownMenu dropdown menu component @hkaikai +- `TDTextarea`: + - add: Added Textarea multiline text box component @hkaikai +- `TDBottomTabBar`: + - add: Support for custom background color and distance between icon and text ([#138](https://github.com/Tencent/tdesign-flutter/issues/138)) + - add: TDBottomTabBar supports externally setting currentIndex ([#110](https://github.com/Tencent/tdesign-flutter/issues/110)) +- `TDBadge`: + - add: TDBadge badge visibility setting when value is 0 @ccXxx1aoBai +- `TDRadio`: + - add: TDRadio added custom background color and text color @ccXxx1aoBai ([#135](https://github.com/Tencent/tdesign-flutter/issues/135)) + - add: Added API to remove left margin ([#128](https://github.com/Tencent/tdesign-flutter/issues/128)) +- `TDCheckbox`: + - add: TDCheckbox added custom text color + - add: Added API to remove left margin +- `TDImage`: + - add: Added Image.file ([#133](https://github.com/Tencent/tdesign-flutter/issues/133)) + - add: Allow external customization of TDImage's fit method ([#114](https://github.com/Tencent/tdesign-flutter/issues/114)) +- `TDInput`: + - add: Added custom size for Input clear button ([#147](https://github.com/Tencent/tdesign-flutter/issues/147)) + - add: Added left margin for label text ([#147](https://github.com/Tencent/tdesign-flutter/issues/147)) + - add: Added rightWidget for carType type ([#147](https://github.com/Tencent/tdesign-flutter/issues/32)) +- `TDDivider`: + - add: Added text style size setting for divider component ([#134](https://github.com/Tencent/tdesign-flutter/issues/134)) +- `TDToast`: + - add: Toast added attribute for custom text length ([#148](https://github.com/Tencent/tdesign-flutter/issues/148)) +- `TDSideBar`: + - add: Added selected style ([#69](https://github.com/Tencent/tdesign-flutter/issues/69)) + - add: Added custom text padding ([#67](https://github.com/Tencent/tdesign-flutter/issues/67)) + +### 🐞 Bug Fixes +- `TDButton`: + - fix: Added mounted judgment before setState() ([#122](https://github.com/Tencent/tdesign-flutter/issues/112)) +- `TDDialog`: + - fix: Modified Dialog to only auto-close when no action is set, if action is set, closing time is handled by the business itself ([#117](https://github.com/Tencent/tdesign-flutter/issues/117)) + +### 🚧 Others +- Added international language adaptation function +- Adapted to 3.16 text centering, added TDTextConfig usage document + + + +## 🌈 0.1.4 `2024-04-08` + +### 🚀 Features +- `TDCountDown`: + - add: Added TDCountDown countdown component @hkaikai +- `TDTheme`: + - add: Modified the theme implementation method, supporting ref attribute for custom mapping + - add: Added default number font numberFontFamily +- `TDText`: + - add: Added TDText force center switch kTextForceVerticalCenterEnable, which can globally disable forced centering to prevent excessive text offset after flutter 3.16 version ([#35](https://github.com/Tencent/tdesign-flutter/issues/35)) +- `TDBottomTabBar`: + - add: Added custom background color feature ([#55](https://github.com/Tencent/tdesign-flutter/issues/55)) +- `TDCheckbox`: + - add: TDCheckbox and TDRadio support custom colors ([#57](https://github.com/Tencent/tdesign-flutter/issues/57)) + - add: TDCheckbox and TDRadio support custom font sizes ([#66](https://github.com/Tencent/tdesign-flutter/issues/66)) +- `TDTabBar`: + - add: TDTabBar adds custom settings for divider color and height ([#71](https://github.com/Tencent/tdesign-flutter/issues/71)) +- `TDSwitch`: + - add: TDSwitch supports custom "on/off" text ([#73](https://github.com/Tencent/tdesign-flutter/issues/73)) +- `TDDialog`: + - add: Added custom title alignment and content Widget feature ([#58](https://github.com/Tencent/tdesign-flutter/issues/58)) + +### 🐞 Bug Fixes +- `TDSlider`: + - fix: Fixed an issue where TDSlider setting showThumbValue does not work. +- `TDButton`: + - fix: Fixed an issue where the external setting of the theme color for TDButton does not take effect ([#54](https://github.com/Tencent/tdesign-flutter/issues/54)) +- `TDInput`: + - fix: Fixed an issue where TDInput's showBottomDivider does not work ([#70](https://github.com/Tencent/tdesign-flutter/issues/70)) + - fix: Removed the invalid height API of TDInput, use SizedBox to modify the height ([#70](https://github.com/Tencent/tdesign-flutter/issues/70)) + +### 🚧 Others +- Example application, added a button to modify the theme, can quickly modify the theme color + + +## 🌈 0.1.3 `2024-03-15` + +### 🚀 Features +- `TDButton`: + - add: Support for customizing the corner radius size through TDButtonStyle.radius +- `TDPicker`: + - add: Picker component scrolling on PC now supports mouse dragging + - add: For TDPicker and TDDatePicker components, the onConfirm no longer defaults to pop up the component internally, allowing external customization; when OnCancel is not empty, the component will not automatically pop. +- `TDSwitch`: + - add: onChanged now supports externally specifying whether to consume the event. If it has been consumed, it will no longer be processed internally ([#27](https://github.com/Tencent/tdesign-flutter/issues/27)) +- `TDBottomTabBar`: + - add: Added custom label text style, optimized labText and icon parameter passing ([#49](https://github.com/Tencent/tdesign-flutter/issues/49)) + +### 🐞 Bug Fixes +- `TDNavBar`: + - fix: The height of NavBar is now obtained in real time to prevent it from not being available at the beginning ([#34](https://github.com/Tencent/tdesign-flutter/issues/34)) +- `TDDialog`: + - fix: The contentColor parameter in DialogInfo was not passed in ([#37](https://github.com/Tencent/tdesign-flutter/pull/37)) +- `TDButton`: + - fix: The click disable effect of TDButton is invalid ([#44](https://github.com/Tencent/tdesign-flutter/issues/44)) +- `TDInput`: + - fix: The delete button inside does not automatically refresh ([#30](https://github.com/Tencent/tdesign-flutter/issues/30)) + - fix: Fixed the mutual exclusion problem between the length of the input content and inputFormatters ([#38](https://github.com/Tencent/tdesign-flutter/issues/38)) +- `TDAlertDialog`: + - fix: The operation of the default button of the component is open ([#40](https://github.com/Tencent/tdesign-flutter/issues/40)) +- `TDRadio`: + - fix: Horizontal arrangement will force the addition of an underline ([#40](https://github.com/Tencent/tdesign-flutter/issues/40)) +- `TDTabBar`: + - fix: The indicatorColor does not take effect ([#31](https://github.com/Tencent/tdesign-flutter/issues/31)) + +### 🚧 Others +- Optimized the performance of commonly used components such as TDButton, TDText, TDTheme, etc. + + +## 🌈 0.1.2 `2024-01-08` + +### 🚀 Features +- `TDImage`: + - add: Added FitWidth type to the image, modified the corresponding Demo page ([#14](https://github.com/Tencent/tdesign-flutter/pull/14)) +- `TDLoading`: + - add: Added methods for showing and hiding loading ([#15](https://github.com/Tencent/tdesign-flutter/pull/15)) +- `TDPopup`: + - add: Added support for customizing the round corners ([#17](https://github.com/Tencent/tdesign-flutter/pull/17)) +- `TDAvatar`: + - add: When the avatar type is character or icon, support for customizing the background color is added ([#20](https://github.com/Tencent/tdesign-flutter/pull/20)) + +### 🐞 Bug Fixes +- `TDBottomTabBar`: + - Added a safe area, fixed ([#1](https://github.com/Tencent/tdesign-flutter/issues/1)) +- `TDButton`: + - update widget: Button's disable status can be updated + - fix: Button click state is too short ([#13](https://github.com/Tencent/tdesign-flutter/pull/13)) +- `TDSwiper`: + - fix: Adapted swiper vertical dot bar style ([#19](https://github.com/Tencent/tdesign-flutter/pull/19)) +- `TDInput`: + - fix: The setting of leftLabelStyle does not take effect when type is TDInputType.twoLine ([#21](https://github.com/Tencent/tdesign-flutter/pull/21)) + +### 🚧 Others +- The minimum compatible version has been changed to 3.7.0 ([#3](https://github.com/Tencent/tdesign-flutter/issues/3)) + +## 0.1.1 +* reset code style, can run on 3.7.x + +## 0.1.0 +* publisher to pub.dev stable + +## 0.0.9 +* update code style + +## 0.0.8 +* update License + +## 0.0.7 +* update example main.dart + +## 0.0.6 +* update slider component, make it is not depend on flutter sdk version + +## 0.0.5 +* publisher to pub.dev + +## 0.0.4 +* fix some bugs + +## 0.0.3 + +* delete default value of TDText's package prop, allow set it null value + +## 0.0.2 + +* update ReadMe.md, modify export file is 'tdesign_flutter.dart' + +## 0.0.1 + +* the first version, add button,text and other components. diff --git a/tdesign-component/LICENSE b/tdesign-component/LICENSE new file mode 100644 index 000000000..85e82560b --- /dev/null +++ b/tdesign-component/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2023-present TDesign + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tdesign-component/README.md b/tdesign-component/README.md new file mode 100644 index 000000000..cd0b6219b --- /dev/null +++ b/tdesign-component/README.md @@ -0,0 +1,70 @@ +

+ + TDesign Logo + +

+ + +Tencent TDesign UI component library of Flutter, suitable for use in mobile projects. + + +[中文文档](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/README_zh.md) + + +# Features: + +- Provides Flutter UI component library in TDesign design style +- Support customizing themes according to App design style +- Provides commonly used Icon library and supports customized replacement +- Define color groups according to the TDesign specification, which can be viewed in TDColors to facilitate the adaptation of components to the TDesign specification. +- The color value declaration class can add default colors and view the default display effect of color values in real time. + + +# Notice: + +- Theme styles such as color/font size/font style/rounded corners/shadow can be configured through json files. Get theme data through `TDTheme.of(context)` or `TDTheme.defaultData()`. It is recommended that all components use `TDTheme.of(context)`Only components that do not need to follow the local theme can use `TDTheme.defaultData()`. + + Examples of usage of colors, fonts, rounded corners, etc.: +``` + TDTheme.of(context).brandNormalColor + TDTheme.defaultData().fontBodyLarge +``` + +- TDesign's icons do not follow the theme, they are all in ttf format, usage examples: +``` + Icon(TDIcons.activity) +``` + +- Example: `example/lib/page/` + +# SDK dependency version: + +dart: ">=2.19.0 <4.0.0" + +flutter: ">=3.7.0" + +# Other technology stack implementations: + +- Desktop Vue 3 implementation:[web-vue-next](https://github.com/Tencent/tdesign-vue-next) +- Desktop React implementation: [web-react](https://github.com/Tencent/tdesign-react) +- Implementation of miniprogram: [miniprogram](https://github.com/Tencent/tdesign-miniprogram) + +# Feedback + + feedback + +# Open source agreement: + +TDesign is licensed under the [MIT LICENSE](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/LICENSE) + + +# Acknowledgements +TDesign Flutter depends on the following component libraries. We appreciate the authors for their open-source contributions: + +[flutter_easyrefresh](https://pub-web.flutter-io.cn/packages/easy_refresh) + +[flutter_swiper](https://pub-web.flutter-io.cn/packages/flutter_swiper) + +[flutter_slidable](https://pub-web.flutter-io.cn/packages/flutter_slidable) + +[image_picker](https://pub-web.flutter-io.cn/packages/image_picker) \ No newline at end of file diff --git a/tdesign-component/README_zh.md b/tdesign-component/README_zh.md new file mode 100644 index 000000000..659d9b939 --- /dev/null +++ b/tdesign-component/README_zh.md @@ -0,0 +1,201 @@ +

+ + TDesign Logo + +

+腾讯TDesign Flutter技术栈组件库,适合在移动端项目中使用。 + +# 特性 + +- 提供TDesign设计风格的Flutter UI组件库 +- 支持根据App设计风格定制主题 +- 提供常用Icon库,支持定制替换 +- 根据TDesign规范定义颜色组,可在TDColors中查看,方便适配TDesign规范的组件 +- 色值声明类可以添加默认颜色,实时查看色值默认显示效果 + +# 使用方法 +- 在pubbspec.yaml引入依赖。 + +```yaml + dependencies: + tdesign_flutter: ^0.0.6 +``` + +- 在文件头部引入:`import 'package:tdesign_flutter/tdesign_flutter.dart'; // 组件库相关的,只需要引入这个文件,里面暴露td前缀所有需要的类` +- 可通过json文件配置颜色/字体尺寸/字体样式/圆角/阴影等主题样式。通过TDTheme.of(context)或者TDTheme.defaultData()获取主题数据。建议组件都使用TDTheme.of(context)的,不需要跟随局部主题的组件,才可以使用TDTheme.defaultData()。 + + 颜色,字体,圆角等使用示例: +``` + TDTheme.of(context).brandNormalColor + TDTheme.defaultData().fontBodyLarge +``` +- TDesign的Icon不跟随主题,都是ttf格式,使用示例: +``` + Icon(TDIcons.activity) +``` + +- 使用示例:`example/lib/page/` + +# 自定义主题 + +## 基础用法 +设置自定义主题的方式: +``` + MaterialApp( + theme: ThemeData( + extensions: [TDThemeData.fromJson('test', testThemeConfig)!], + ) + …… + ) +``` +自定义主题属性,常用可设置属性键值请参考[td_default_theme.dart](lib/src/theme/td_default_theme.dart): +``` + String testThemeConfig = ''' + { + "test": { + "color": { + "brandNormalColor": "#D7B386" + }, + "font": { + "fontBodyMedium": { + "size": 40, + "lineHeight": 55 + } + } + } + } + '''; +``` + +## 主题生成器 +如果你不想自定义太多颜色,但是想要拥有好看的自定义主题,"主题生成器"是个不错的选择. + +1.进入[TDesign官网](https://tdesign.tencent.com/vue/custom-theme) ,点击下方的主题生成器,然后再右边生成器里选择想要的颜色,点击下载 + +![img.png](../tdesign-site/site/public/assets/theme_generator.png) + +![img.png](../tdesign-site/site/public/assets/select_color.png) + +2.此时你得到是一个theme.css文件,可以将该文件放到tdesign-component/example/shell/theme/文件夹下,把该文件夹下的css2JsonTheme.dart修改为你自己的文件名、主题名和输出路径,即可得到一个theme.json文件 +![img.png](../tdesign-site/site/public/assets/dart_modify.png) + +3.将主题json加载进TDTheme,美观的自定义主题就设置完成了. +``` + var jsonString = await rootBundle.loadString('assets/theme.json'); + var _themeData = TDThemeData.fromJson('green', jsonString); + // …… + MaterialApp( + title: 'TDesign Flutter Example', + theme: ThemeData( + extensions: [_themeData] + ), + home: MyHomePage(title: 'TDesign Flutter 组件库'), + ); +``` + +# 国际化 +TD组件库内部不内置国际化语言,但支持与flutter的国际化能力搭配使用.可以继承TDResourceDelegate类,该类抽离了组件内部所有文字资源,重新获取文字的方法,进行国际化处理,并通过 TDTheme.setResourceBuilder 注入. +示例代码: + +1. 重写TDResourceDelegate类: +``` +/// 国际化资源代理 +class IntlResourceDelegate extends TDResourceDelegate { + IntlResourceDelegate(this.context); + + BuildContext context; + + /// 国际化需要每次更新context + updateContext(BuildContext context){ + this.context = context; + } + + @override + String get cancel => AppLocalizations.of(context)!.cancel; + + @override + String get confirm => AppLocalizations.of(context)!.confirm; + +} +``` + + +2.注入TDResourceDelegate类: +``` + var delegate = IntlResourceDelegate(context); + return MaterialApp( + home: Builder( + builder: (context) { + // 设置文案代理,国际化需要在MaterialApp初始化完成之后才生效,而且需要每次更新context + TDTheme.setResourceBuilder((context) => delegate..updateContext(context), needAlwaysBuild: true); + return MyHomePage( + title: AppLocalizations.of(context)?.components ?? '', + ); + }, + ), + // 设置国际化处理 + locale: locale, + supportedLocales: AppLocalizations.supportedLocales, + localizationsDelegates: AppLocalizations.localizationsDelegates, + ); +``` + +3.flutter国际化配置方法,官方文档:[Flutter 应用里的国际化](https://docs.flutter.cn/ui/accessibility-and-internationalization/internationalization) + +# 开发规范 +- 组件命名规范:以TD为前缀,组件名称、API名称参考TDesign现有组件和API命名,可以根据flutter原生Widget的特点进行修改。组件API以满足设计要求和使用为准,可根据flutter特点做精简或定制。 +- 组件库用到的所有色值、圆角、字体字号等样式属性需全部定义在主题中。 +- 代码规范遵循腾讯Dart代码规范。 +- 对于系统原有组件,如Text,Image等,应兼容系统原组件功能,只能扩展,不能阉割,以免业务需要使用系统功能时,必须放弃TDesign控件。 +- 示例页面尽量使用ExamplePage+ExampleModule+ExampleItem组合,按照示例稿的布局实现;页面写完后,在main.dart中修改exampleMap对应组件的isTodo属性即可。 +- 组件API和演示代码,请参考`demo_tool/README.md`文件。 +- 组件内部的固定文案,都应该抽离到TDResourceDelegate中统一管理,方便业务进行国际化适配 + +# 共建流程 +- 拉取开发分支:建议将项目fork到自己github,每个组件从main分支拉取对应开发分支,命名为feature/组件名小写_下划线 +- 实现组件:组件中的属性请尽量使用TDTheme提供的公共属性,使用方法参考'主题-颜色'页面 +- 编写示例页:示例页请尽量使用ExamplePage+ExampleModule+ExampleItem组合,参考示例稿布局实现。 +- 演示代码:每个组件示例,尽量将原子性代码提取成独立方法,并添加@Demo注解,方便生成演示代码。其中,@Demo注解的'group'参数需与ExamplePage的'exampleCodeGroup'参数一致。写法请参考'圆角-基础'页。 +- flutterAOP: 如果可以,建议切换到flutter 3.10.0分支,并添加AOP补丁,生成演示代码。 +- API文档:API文档由工具统一生成,请尽量添加字段的详细注释,并将构造方法作为类名下的第一个方法,字段放在构造方法之下,具体写法请参考TDText。 +- 代码规范:开发完成后,请检查'Dart Analysis'下的提示,尽量符合代码规范。 +- 单元测试:添加未在示例稿中体现,但有必要验证的组件样式,请添加到ExamplePage的'test'参数中。 +- 合并代码: 上述检查完成后,请发起pr,合并到dev分支,并同步项目组验收。 + +## 常见问题 + +- 文本居中: + > 0.1.4版本:Flutter 3.16之后,修改了渲染引擎,导致启用forceVerticalCenter参数的组件字体偏移更多,不再居中.可以通过设置kTextForceVerticalCenterEnable=false来禁用字体居中功能,让组件显示与官方Text一致 + > + > 0.1.5版本:适配了Android和iOS双端基础系统字体的中文居中,其他语言的字体,可以通过重写TDTextPaddingConfig的paddingRate和paddingExtraRate进行自定义适配,TDTextPaddingConfig使用方法可参考TDTextPage. + +- 修改全局字体: +> 设置kTextNeedGlobalFontFamily=true,然后设置TDTextConfiguration的globalFontFamily参数.(0.1.6版本开始支持) + +# SDK依赖版本 +dart: ">=2.19.0 <4.0.0" + +flutter: ">=3.7.0" + +# 其他技术栈实现 +- 桌面端 Vue 3 实现:[web-vue-next](https://github.com/Tencent/tdesign-vue-next) +- 桌面端 React 实现: [web-react](https://github.com/Tencent/tdesign-react) +- 移动端小程序实现: [小程序](https://github.com/Tencent/tdesign-miniprogram) + +# 交流反馈 + + feedback + + +# 开源协议 + +TDesign 遵循 [MIT 协议](https://github.com/Tencent/tdesing-flutter/blob/main/tdesign-component/LICENSE) + +# 致谢 +TDesign Flutter 依赖以下组件库,感谢作者的开源贡献: + +[flutter_easyrefresh](https://pub-web.flutter-io.cn/packages/easy_refresh) + +[flutter_swiper](https://pub-web.flutter-io.cn/packages/flutter_swiper) + +[flutter_slidable](https://pub-web.flutter-io.cn/packages/flutter_slidable) \ No newline at end of file diff --git a/tdesign-component/analysis_options.yaml b/tdesign-component/analysis_options.yaml new file mode 100644 index 000000000..36af5adae --- /dev/null +++ b/tdesign-component/analysis_options.yaml @@ -0,0 +1,208 @@ +analyzer: + enable-experiment: + - extension-methods + errors: + unused_import: ignore + camel_case_types: warning + missing_required_param: error + missing_return: warning + todo: ignore + non_constant_identifier_names: warning + constant_identifier_names: warning + library_names: warning + file_names: warning + avoid_setters_without_getters: warning + avoid_return_types_on_setters: warning + avoid_returning_null: warning + avoid_returning_this: warning + prefer_generic_function_type_aliases: warning + always_declare_return_types: warning + hash_and_equals: warning + avoid_equals_and_hash_code_on_mutable_classes: warning + avoid_null_checks_in_equality_operators: warning + prefer_interpolation_to_compose_strings: warning + prefer_collection_literals: warning + prefer_is_empty: warning + unnecessary_lambdas: warning + prefer_equal_for_default_values: warning + avoid_init_to_null: warning + +linter: + rules: + - avoid_unnecessary_containers + - no_logic_in_create_state + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors + - use_key_in_widget_constructors + # 1.11 使用大驼峰命名类型 + - camel_case_types + # 1.11 使用大驼峰命名拓展 + - camel_case_extensions + # 1.12 类成员,顶级定义,变量,参数,命名参数和命名构造函数 + - non_constant_identifier_names + # 1.12 常量变量,包括枚举的名称规范 + - constant_identifier_names + #倒入import library的名字规范 + - library_names + #1.13 import dart文件的规范 + - file_names + #1.21 【可选】顺序 + - directives_ordering + #2.22 【推荐】 公开接口要有注释 + - package_api_docs + #3.32【必须】组合字符串统一使用插值的形式${param},并避免在字符串插值中使用不必要的大括号 + - prefer_interpolation_to_compose_strings + #3.41 【必须】尽可能使用字面量初始化集合 + - prefer_collection_literals + #3.42【必须】使用.isEmpty/.isNotEmpty判空 + - prefer_is_empty + #3.51 【必须】不要使用lambda表达式来替代tear-off + - unnecessary_lambdas + #3.61 【必须】参数默认值用=号 + - prefer_equal_for_default_values + #3.71 【必须】不要把变量初始化为null + - avoid_init_to_null + #3.82 【必须】去掉不必要的setter和getter + - unnecessary_getters_setters + #4.35 mixin的使用规范 + - prefer_mixin + #4.54【必须】避免单独使用setter或者getters + - avoid_setters_without_getters + #4.55【必须】不给setter方法指定返回值 + - avoid_return_types_on_setters + #4.56【必须】基础数据类型的返回值不要返回null + - avoid_returning_null + #4.57 【必须】不要返回this来实现链式调用 + - avoid_returning_this + #4.61 【必须】添加必要的类型注解 TODO 讨论是否有替代方案 + # - type_annotate_public_apis + #4.66 【必须】不要使用旧的typedef语法 + - prefer_generic_function_type_aliases + #4.67 【必须】正确使用Future返回值 TODO 这个lint是说所有方法都必须需要返回类型而不是仅仅Future + # - always_declare_return_types + #4.81 【必须】必须重写 hashCode 方法 + - hash_and_equals + #4.83 【必须】可变类不要重载== + - avoid_equals_and_hash_code_on_mutable_classes + #4.84 【必须】重载==时不必判空 + - avoid_null_checks_in_equality_operators + # 声明返回类型 TODO============ 以下规则对齐codecc代码扫描 ===================================== + # - always_declare_return_types + # separate the control structure expression from its statement + - always_put_control_body_on_new_line + # 声明了@required的参数,需要有assert(param != null) + - always_require_non_null_named_parameters + # 注释重写的方法和参数 + - annotate_overrides + # 避免空的else + - avoid_empty_else + # 避免使用print方法 + # - avoid_print + # 避免以相对路径方式引入文件 + - avoid_relative_lib_imports + # 避免返回null给future + - avoid_returning_null_for_future + # 避免复写相同的参数类型 + - avoid_shadowing_type_parameters + # 避免使用同步的文件操作 + - avoid_slow_async_io + # 避免参数名和类型名相同 + - avoid_types_as_parameter_names + # 避免使用web的库在flutter终端中,这些库不支持在web以外使用 + - avoid_web_libraries_in_flutter + # dart.async.StreamSubscription的实例不用时,触发cancel方法 + - cancel_subscriptions + # dart.core.Sink的实例不用时,触发cancel方法 + - close_sinks + # 评论只引用在作用域的变量 + - comment_references + # 避免在finally语句中结束控制流 + - control_flow_in_finally + # 控制结构用大括号来区分,除非没有else语句且可放在一行的 + - curly_braces_in_flow_control_structures + # 在debug方法中引用所有的公共属性 + # - diagnostic_describe_all_properties + # 避免空的catch + - empty_catches + # 用;替代空的构造方法体 + - empty_constructor_bodies + # 避免使用空语句 + - empty_statements + # 不要出现可以在编译阶段推算出恒true或者false的条件判断 + - invariant_booleans + # 当contains方法的参数和可枚举的类型不同,不要触发可枚举类型的contains方法 + - iterable_contains_unrelated_type + # 用小写加下划线做库的命名 + - library_prefixes + # 当remove方法的参数和List的类型不同,不要触发remove方法 + - list_remove_unrelated_type + # 不要出现只有一种情况的boolean表达式 + - literal_only_boolean_expressions + # 不要在list内用临近字符串方式 + - no_adjacent_strings_in_list + # 不要用重复值的case语句 + - no_duplicate_case_values + # 不要传null值给闭包语句 + - null_closures + # 省略对本地参数的类型声明 + - omit_local_variable_types + # 用相邻的string去减少string操作符 + - prefer_adjacent_string_concatenation + # 尽量用??=来判定null + - prefer_conditional_assignment + # 不要用indexOf来判定一个集合是否包含某元素 + - prefer_contains + # 用final来声明在类中不会被改变的私有参数 + - prefer_final_fields + # 当由iterables构建map时,用for语句 + - prefer_for_elements_to_map_fromIterable + # 用?? 替代null的检查 + - prefer_if_null_operators + # 用isNotEmpty替代!isEmpty + - prefer_is_not_empty + # 用iterable.whereType() 替代 iterable.where((e) => e is T). + - prefer_iterable_whereType + # 在lib路径下,建议使用相对路径引入 + - prefer_relative_imports + # 用单引号替代双引号 + - prefer_single_quotes + # 尽量用扩展符 + - prefer_spread_collections + # 在void可以的时候不要用null + - prefer_void_to_null + # 多余的getter + - recursive_getters + # 用/// 作为注释 + - slash_for_doc_comments + # 在重载==运算符时,判定入参的类型 + - test_types_in_equals + # 避免在finally语句中抛出错误 + - throw_in_finally + # 不要对已申明类型的变量在初始化时再声明 + - type_init_formals + # 在async的方法中await返回为future的方法 + - unawaited_futures + # 避免重复的const语句 + - unnecessary_const + # 避免在创建对象时用new + - unnecessary_new + # 避免在if null语句中用null + - unnecessary_null_in_if_null_operators + # 避免不必要的语句 + - unnecessary_statements + # 当不需要避免重复时不要用this + - unnecessary_this + # 避免无关类型的相等检查 + - unrelated_type_equality_checks + # 避免直接插入html + - unsafe_html + # 用类函数语法作为参数 + - use_function_type_syntax_for_parameters + # 用rethrow去重新抛出错误 + - use_rethrow_when_possible + # 用正确的正则去创建正则 + - valid_regexps diff --git a/tdesign-component/assets/tdesign/TCloudNumberVF.ttf b/tdesign-component/assets/tdesign/TCloudNumberVF.ttf new file mode 100644 index 000000000..6abfb9255 Binary files /dev/null and b/tdesign-component/assets/tdesign/TCloudNumberVF.ttf differ diff --git a/tdesign-component/assets/tdesign/td_icons.ttf b/tdesign-component/assets/tdesign/td_icons.ttf new file mode 100644 index 000000000..b17bf764a Binary files /dev/null and b/tdesign-component/assets/tdesign/td_icons.ttf differ diff --git a/tdesign-component/demo_tool/README.md b/tdesign-component/demo_tool/README.md new file mode 100644 index 000000000..7cdf48e4d --- /dev/null +++ b/tdesign-component/demo_tool/README.md @@ -0,0 +1,87 @@ +# api_tool +# 基于smart_cli实现的组件库生成工具 + +## 组件注释规范 +### 注:生成工具有待完善,目前先按工具代码规范编写代码,如有不满足的场景,再修改工具 +编写规范需注意: +- 构造方法为类名下的第一行代码,且不能有注释。成员字段需写在构造方法后面 +- 成员变量的注释需要用///,不能用// +- 构造方法不能标注@override + +#### 组件widget注释示例: +``` +/// 组件简介(必须) +``` +#### 组件属性注释示例: +``` +/// 属性简介(必须) +``` + + +## 组件库工具使用方法 +### 初始化工具调用命令 +``` +./bin/api_tool_xxx generate + --file 相对ui_component目录的组件文件路径 + --folder 相对ui_component目录的组件文件夹路径 + --name 组件名,多个组件名之间用英文,分割 + --folder-name [可选]生成的组件示例文件夹名称,默认生成的文件夹名称是第一个name参数的下划线表示 + --[no-]only-api 是否只更新api文件 + --[no-]use-grammar 是否采用语法分析器,默认采用词法分析 +``` +--- +### 一、 初始化命令 + +【前置】:在demo_tool/version中填入对应dart_sdk的版本号 + +初始化命令有以下 3 种使用方式: + +1、初始化一个组件文件中的一个组件示例,没有--folder-name的时候,默认文件夹名称是第一个name的下划线表示,示例: + +``` +./demo_tool/bin/api_tool_xxx generate --file lib/src/components/tags/td_tag.dart --name TDTag --folder-name tag --only-api +``` + +2、 把一个文件中的多个组件合并生成一份示例数据(api说明生成在一个文件中),没有--folder-name的时候,默认文件夹名称是第一个name的下划线表示 +``` +./demo_tool/bin/api_tool_xxx generate --file lib/checkbox/custom_check_box.dart --name SquareCheckbox,TECheckBox --folder-name checkbox2 +``` +3、 把一个文件夹中的多个组件合并生成一份示例数据(api说明生成在一个文件中),没有--folder-name的时候,默认文件夹名称是第一个name的下划线表示 +``` +./demo_tool/bin/api_tool_xxx generate --folder lib/setting --name SettingItemWidget,SettingTowRowCellWidget,SettingLeftTextCellWidget,SettingCheckBoxCellWidget,SettingTowTextCellWidget,SettingTowLineTextCellWidget,SettingGroupWidget,SettingGroupTextWidget --folder-name setting +``` + +如果想只更新API文档,那么在上述初始化的命令之后增加参数 `--only-api` 即可 + +默认采用词法分析,如果想采用语法分析的方式生成代码,那么在上述初始化的命令之后增加参数 `--use-grammar` 即可 +` + +# 演示代码 +## 生成逻辑 +演示代码依赖FlutterAOP能力,通过解析@Demo注解所在的方法自动生成。因此组件示例的写法,要求将可显示的部份提取成独立方法,并添加@Demo注解,示例: +``` + @override + Widget build(BuildContext context) { + return ExamplePage( + exampleCodeGroup:'button', + children: [ + ExampleModule(title: '默认', + children: [ + ExampleItem(desc: '可点击', builder: _buildNormalClickButton) + ]) + ]); + } + + @Demo(group: 'button') + TDButton _buildNormalClickButton(BuildContext context) { + return TDButton(content: '强按钮', + style: TDButtonStyle.primary(), + onTap: onTap, + onLongPress: onLongPress, + ); + } +``` +其中,`group`参数需与`exampleCodeGroup`参数一致,为直接的字符串赋值,不能是变量引用或者字符串拼接。 + +## Flutter AOP +提交pr后,将会触发流水线自动打包apk,流水线配置了AOP能力 \ No newline at end of file diff --git a/tdesign-component/demo_tool/all_build.sh b/tdesign-component/demo_tool/all_build.sh new file mode 100644 index 000000000..0fe82b4f7 --- /dev/null +++ b/tdesign-component/demo_tool/all_build.sh @@ -0,0 +1,143 @@ +# 基础 +# button +./bin/api_tool_linux generate --folder ../lib/src/components/button --name TDButton,TDButtonStyle --folder-name button --output ../example/assets/api/ --only-api +# divider +./bin/api_tool_linux generate --file ../lib/src/components/divider/td_divider.dart --name TDDivider --folder-name divider --output ../example/assets/api/ --only-api +# fab +./bin/api_tool_linux generate --file ../lib/src/components/fab/td_fab.dart --name TDFab --folder-name fab --output ../example/assets/api/ --only-api +# icon +./bin/api_tool_linux generate --file ../lib/src/components/icon/td_icons.dart --name TDIcons --folder-name icon --output ../example/assets/api/ --only-api +# link +./bin/api_tool_linux generate --file ../lib/src/components/link/td_link.dart --name TDLink --folder-name link --output ../example/assets/api/ --only-api +# text +./bin/api_tool_linux generate --file ../lib/src/components/text/td_text.dart --name TDText,TDTextSpan,TDTextConfiguration --folder-name text --output ../example/assets/api/ --only-api + + +# 导航 +# back_top +./bin/api_tool_linux generate --file ../lib/src/components/backtop/td_backtop.dart --name TDBackTop --folder-name back-top --output ../example/assets/api/ --only-api +# drawer +./bin/api_tool_linux generate --folder ../lib/src/components/drawer --name TDDrawer,TDDrawerWidget,TDDrawerItem,TDDrawerStyle --folder-name drawer --output ../example/assets/api/ --only-api --get-comments +# indexes +./bin/api_tool_linux generate --folder ../lib/src/components/indexes --name TDIndexes,TDIndexesAnchor,TDIndexesList --folder-name indexes --output ../example/assets/api/ --only-api --get-comments +# navbar +./bin/api_tool_linux generate --file ../lib/src/components/navbar/td_nav_bar.dart --name TDNavBar,TDNavBarItem, --folder-name navbar --output ../example/assets/api/ --only-api +# sidebar +./bin/api_tool_linux generate --folder ../lib/src/components/sidebar --name TDSideBar,TDSideBarItem, --folder-name side-bar --output ../example/assets/api/ --only-api +# steps +./bin/api_tool_linux generate --folder ../lib/src/components/steps --name TDSteps,TDStepsItemData --folder-name steps --output ../example/assets/api/ --only-api +# tabbar +./bin/api_tool_linux generate --file ../lib/src/components/tabbar/td_bottom_tab_bar.dart --name TDBottomTabBar,BadgeConfig,TDBottomTabBarTabConfig,TDBottomTabBarPopUpBtnConfig,TDBottomTabBarPopUpShapeConfig,PopUpMenuItem --folder-name tab-bar --output ../example/assets/api/ --only-api +# tabs +./bin/api_tool_linux generate --folder ../lib/src/components/tabs --name TDTabBar,TDTab,TDTabBarView --folder-name tabs --output ../example/assets/api/ --only-api + + +# 输入 +# calendar +./bin/api_tool_linux generate --folder ../lib/src/components/calendar --name TDCalendar,TDCalendarPopup,TDCalendarStyle --folder-name calendar --output ../example/assets/api/ --only-api +# cascader +./bin/api_tool_linux generate --folder ../lib/src/components/cascader --name TDMultiCascader --folder-name cascader --output ../example/assets/api/ --only-api + +# checkbox +./bin/api_tool_linux generate --folder ../lib/src/components/checkbox --name TDCheckbox,TDCheckboxGroup --folder-name checkbox --output ../example/assets/api/ --only-api +# date_picker +./bin/api_tool_linux generate --folder ../lib/src/components/picker --name TDPicker,TDDatePicker --folder-name date-time-picker --output ../example/assets/api/ --only-api +# form +./bin/api_tool_linux generate --folder ../lib/src/components/form --name TDForm,TDFormItem,TDFormItemType,TDFormValidation --folder-name form --output ../example/assets/api/ --only-api +# input +./bin/api_tool_linux generate --file ../lib/src/components/input/td_input.dart --name TDInput, TDInputSpacer --folder-name input --output ../example/assets/api/ --only-api +# picker +./bin/api_tool_linux generate --folder ../lib/src/components/picker --name TDPicker,TDMultiPicker,TDMultiLinkedPicker,MultiLinkedPickerModel --folder-name picker --output ../example/assets/api/ --only-api +# radio +./bin/api_tool_linux generate --file ../lib/src/components/radio/td_radio.dart --name TDRadioStyle,TDRadio,TDRadioGroup --folder-name radio --output ../example/assets/api/ --only-api --get-comments +# rate +./bin/api_tool_linux generate --file ../lib/src/components/rate/td_rate.dart --name TDRate --folder-name rate --output ../example/assets/api/ --only-api +# search +./bin/api_tool_linux generate --file ../lib/src/components/search/td_search_bar.dart --name TDSearchBar --folder-name search --output ../example/assets/api/ --only-api +# slider +./bin/api_tool_linux generate --file ../lib/src/components/slider/td_slider.dart --name TDSlider,TDRangeSlider,TDSliderThemeData --folder-name slider --output ../example/assets/api/ --only-api +# stepper +./bin/api_tool_linux generate --file ../lib/src/components/stepper/td_stepper.dart --name TDStepper --folder-name stepper --output ../example/assets/api/ --only-api +# switch +./bin/api_tool_linux generate --file ../lib/src/components/switch/td_switch.dart --name TDSwitch --folder-name switch --output ../example/assets/api/ --only-api +# textarea +./bin/api_tool_linux generate --file ../lib/src/components/textarea/td_textarea.dart --name TDTextarea --folder-name textarea --output ../example/assets/api/ --only-api --get-comments +# tree_select +./bin/api_tool_linux generate --file ../lib/src/components/tree/td_tree_select.dart --name TDTreeSelect,TDSelectOption --folder-name tree-select --output ../example/assets/api/ --only-api + +# upload +./bin/api_tool_linux generate --file ../lib/src/components/upload/td_upload.dart --name TDUpload --folder-name upload --output ../example/assets/api/ --only-api + + +# 数据展示 +# avatar +./bin/api_tool_linux generate --file ../lib/src/components/avatar/td_avatar.dart --name TDAvatar --folder-name avatar --output ../example/assets/api/ --only-api +# badge +./bin/api_tool_linux generate --file ../lib/src/components/badge/td_badge.dart --name TDBadge --folder-name badge --output ../example/assets/api/ --only-api +# cell +./bin/api_tool_linux generate --folder ../lib/src/components/cell --name TDCell,TDCellGroup,TDCellStyle --folder-name cell --output ../example/assets/api/ --only-api --get-comments +# timeCounter +./bin/api_tool_linux generate --folder ../lib/src/components/time_counter --name TDTimeCounter,TDTimeCounterController,TDTimeCounterStyle --folder-name time-counter --output ../example/assets/api/ --only-api --get-comments +# collapse +./bin/api_tool_linux generate --folder ../lib/src/components/collapse --name TDCollapse --folder-name collapse --output ../example/assets/api/ --only-api --get-comments + +# empty +./bin/api_tool_linux generate --file ../lib/src/components/empty/td_empty.dart --name TDEmpty --folder-name empty --output ../example/assets/api/ --only-api +# footer +./bin/api_tool_linux generate --file ../lib/src/components/footer/td_footer.dart --name TDFooter,TDFooterType --folder-name footer --output ../example/assets/api/ --only-api + +# grid +# image +./bin/api_tool_linux generate --file ../lib/src/components/image/td_image.dart --name TDImage --folder-name image --output ../example/assets/api/ --only-api +# imageViewer +./bin/api_tool_linux generate --folder ../lib/src/components/image_viewer --name TDImageViewer,TDImageViewerWidget, --folder-name image-viewer --output ../example/assets/api/ --only-api +# progress +./bin/api_tool_linux generate --file ../lib/src/components/progress/td_progress.dart --name TDProgress --folder-name progress --output ../example/assets/api/ --only-api +# result +./bin/api_tool_linux generate --file ../lib/src/components/result/td_result.dart --name TDResult --folder-name result --output ../example/assets/api/ --only-api +# skeleton +./bin/api_tool_linux generate --folder ../lib/src/components/skeleton --name TDSkeleton,TDSkeletonRowColStyle,TDSkeletonRowCol,TDSkeletonRowColObjStyle,TDSkeletonRowColObj --folder-name skeleton --output ../example/assets/api/ --only-api + +# sticky +# swiper +./bin/api_tool_linux generate --folder ../lib/src/components/swiper --name TDSwiperPagination,TDPageTransformer --folder-name swiper --output ../example/assets/api/ --only-api --get-comments +# table +./bin/api_tool_linux generate --folder ../lib/src/components/table --name TDTable,TDTableCol,TDTableEmpty --folder-name table --output ../example/assets/api/ --only-api +# tag +./bin/api_tool_linux generate --folder ../lib/src/components/tag --name TDTag,TDSelectTag,TDTagStyle --folder-name tag --output ../example/assets/api/ --only-api + + + +# 反馈 +# action_sheet +./bin/api_tool_linux generate --folder ../lib/src/components/action_sheet --name TDActionSheetItem,TDActionSheet --folder-name action-sheet --output ../example/assets/api/ --only-api --get-comments +# dialog +./bin/api_tool_linux generate --folder ../lib/src/components/dialog --name TDAlertDialog,TDConfirmDialog,TDDialogButtonOptions,TDDialogButtonStyle,TDDialogScaffold,TDDialogTitle,TDDialogContent,TDDialogInfoWidget,HorizontalNormalButtons,HorizontalTextButtons,TDDialogButton,TDDialogImagePosition,TDImageDialog,TDInputDialog --folder-name dialog --output ../example/assets/api/ --only-api +# dropdown_menu +./bin/api_tool_linux generate --folder ../lib/src/components/dropdown_menu --name TDDropdownMenu,TDDropdownMenuDirection,TDDropdownItem,TDDropdownItemOption --folder-name dropdown-menu --output ../example/assets/api/ --only-api --get-comments +# loading +./bin/api_tool_linux generate --file ../lib/src/components/loading/td_loading.dart --name TDLoading --folder-name loading --output ../example/assets/api/ --only-api +# message +./bin/api_tool_linux generate --file ../lib/src/components/message/td_message.dart --name TDMessage,MessageTheme,MessageMarquee,MessageLink --folder-name message --output ../example/assets/api/ --only-api +# noticeBar +./bin/api_tool_linux generate --folder ../lib/src/components/notice_bar --name TDNoticeBar,TDNoticeBarStyle --folder-name notice-bar --output ../example/assets/api/ --only-api --get-comments +# overlay +# popover +./bin/api_tool_linux generate --folder ../lib/src/components/popover --name TDPopover,TDPopoverWidget --folder-name popover --output ../example/assets/api/ --only-api --get-comments +# popup +./bin/api_tool_linux generate --folder ../lib/src/components/popup --name TDSlidePopupRoute,TDPopupBottomDisplayPanel,TDPopupBottomConfirmPanel,TDPopupCenterPanel --folder-name popup --output ../example/assets/api/ --only-api --get-comments +# refresh +./bin/api_tool_linux generate --file ../lib/src/components/refresh/td_refresh_header.dart --name TDRefreshHeader --folder-name pull-down-refresh --output ../example/assets/api/ --only-api --get-comments +# swipecell +./bin/api_tool_linux generate --folder ../lib/src/components/swipe_cell --name TDSwipeAction,TDSwipeAutoClose,TDSwipeCell,TDSwipePanel --folder-name swipe-cell --output ../example/assets/api/ --only-api --get-comments +# toast +./bin/api_tool_linux generate --file ../lib/src/components/toast/td_toast.dart --name TDToast --folder-name toast --output ../example/assets/api/ --only-api + + +# 其他 +# theme +./bin/api_tool_linux generate --file ../lib/src/theme/td_theme.dart --name TDTheme,TDThemeData --folder-name theme --output ../example/assets/api/ --only-api +# radius + + + diff --git a/tdesign-component/demo_tool/bin/api_tool_linux b/tdesign-component/demo_tool/bin/api_tool_linux new file mode 100755 index 000000000..6ddd2da58 Binary files /dev/null and b/tdesign-component/demo_tool/bin/api_tool_linux differ diff --git a/tdesign-component/demo_tool/bin/api_tool_mac b/tdesign-component/demo_tool/bin/api_tool_mac new file mode 100755 index 000000000..8ddea54a0 Binary files /dev/null and b/tdesign-component/demo_tool/bin/api_tool_mac differ diff --git a/tdesign-component/demo_tool/bin/api_tool_windows b/tdesign-component/demo_tool/bin/api_tool_windows new file mode 100644 index 000000000..7804b1c64 Binary files /dev/null and b/tdesign-component/demo_tool/bin/api_tool_windows differ diff --git a/tdesign-component/demo_tool/version b/tdesign-component/demo_tool/version new file mode 100644 index 000000000..eea66454c --- /dev/null +++ b/tdesign-component/demo_tool/version @@ -0,0 +1 @@ +3.16.9 \ No newline at end of file diff --git a/tdesign-component/example/.gitignore b/tdesign-component/example/.gitignore new file mode 100644 index 000000000..74086e9c7 --- /dev/null +++ b/tdesign-component/example/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release +/assets/api/ diff --git a/example/.metadata b/tdesign-component/example/.metadata similarity index 100% rename from example/.metadata rename to tdesign-component/example/.metadata diff --git a/example/README.md b/tdesign-component/example/README.md similarity index 100% rename from example/README.md rename to tdesign-component/example/README.md diff --git a/tdesign-component/example/analysis_options.yaml b/tdesign-component/example/analysis_options.yaml new file mode 100644 index 000000000..8362cdf9a --- /dev/null +++ b/tdesign-component/example/analysis_options.yaml @@ -0,0 +1,206 @@ +analyzer: + enable-experiment: + - extension-methods + errors: + unused_import: ignore + camel_case_types: warning + missing_required_param: error + missing_return: warning + todo: ignore + non_constant_identifier_names: warning + constant_identifier_names: warning + library_names: warning + file_names: warning + avoid_setters_without_getters: warning + avoid_return_types_on_setters: warning + avoid_returning_null: warning + avoid_returning_this: warning + prefer_generic_function_type_aliases: warning + always_declare_return_types: warning + hash_and_equals: warning + avoid_equals_and_hash_code_on_mutable_classes: warning + avoid_null_checks_in_equality_operators: warning + prefer_interpolation_to_compose_strings: warning + prefer_collection_literals: warning + prefer_is_empty: warning + unnecessary_lambdas: warning + prefer_equal_for_default_values: warning + avoid_init_to_null: warning + +linter: + rules: + - avoid_unnecessary_containers + - no_logic_in_create_state + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors + - use_key_in_widget_constructors + # 1.11 使用大驼峰命名类型 + - camel_case_types + # 1.11 使用大驼峰命名拓展 + - camel_case_extensions + # 1.12 类成员,顶级定义,变量,参数,命名参数和命名构造函数 + - non_constant_identifier_names + # 1.12 常量变量,包括枚举的名称规范 + - constant_identifier_names + #倒入import library的名字规范 + - library_names + #1.13 import dart文件的规范 + - file_names + #1.21 【可选】顺序 + - directives_ordering + #2.22 【推荐】 公开接口要有注释 + - package_api_docs + #3.32【必须】组合字符串统一使用插值的形式${param},并避免在字符串插值中使用不必要的大括号 + - prefer_interpolation_to_compose_strings + #3.41 【必须】尽可能使用字面量初始化集合 + - prefer_collection_literals + #3.42【必须】使用.isEmpty/.isNotEmpty判空 + - prefer_is_empty + #3.51 【必须】不要使用lambda表达式来替代tear-off + - unnecessary_lambdas + #3.61 【必须】参数默认值用=号 + - prefer_equal_for_default_values + #3.71 【必须】不要把变量初始化为null + - avoid_init_to_null + #3.82 【必须】去掉不必要的setter和getter + - unnecessary_getters_setters + #4.35 mixin的使用规范 + - prefer_mixin + #4.54【必须】避免单独使用setter或者getters + - avoid_setters_without_getters + #4.55【必须】不给setter方法指定返回值 + - avoid_return_types_on_setters + #4.56【必须】基础数据类型的返回值不要返回null + - avoid_returning_null + #4.57 【必须】不要返回this来实现链式调用 + - avoid_returning_this + #4.61 【必须】添加必要的类型注解 TODO 讨论是否有替代方案 + # - type_annotate_public_apis + #4.66 【必须】不要使用旧的typedef语法 + - prefer_generic_function_type_aliases + #4.67 【必须】正确使用Future返回值 TODO 这个lint是说所有方法都必须需要返回类型而不是仅仅Future + # - always_declare_return_types + #4.81 【必须】必须重写 hashCode 方法 + - hash_and_equals + #4.83 【必须】可变类不要重载== + - avoid_equals_and_hash_code_on_mutable_classes + #4.84 【必须】重载==时不必判空 + - avoid_null_checks_in_equality_operators + # 声明返回类型 TODO============ 以下规则对齐codecc代码扫描 ===================================== + # - always_declare_return_types + # separate the control structure expression from its statement + - always_put_control_body_on_new_line + # 声明了@required的参数,需要有assert(param != null) + - always_require_non_null_named_parameters + # 注释重写的方法和参数 + - annotate_overrides + # 避免空的else + - avoid_empty_else + # 避免使用print方法 + # - avoid_print + # 避免以相对路径方式引入文件 + - avoid_relative_lib_imports + # 避免返回null给future + - avoid_returning_null_for_future + # 避免复写相同的参数类型 + - avoid_shadowing_type_parameters + # 避免使用同步的文件操作 + - avoid_slow_async_io + # 避免参数名和类型名相同 + - avoid_types_as_parameter_names + # 避免使用web的库在flutter终端中,这些库不支持在web以外使用 + - avoid_web_libraries_in_flutter + # dart.async.StreamSubscription的实例不用时,触发cancel方法 + - cancel_subscriptions + # dart.core.Sink的实例不用时,触发cancel方法 + - close_sinks + # 评论只引用在作用域的变量 + - comment_references + # 避免在finally语句中结束控制流 + - control_flow_in_finally + # 控制结构用大括号来区分,除非没有else语句且可放在一行的 + - curly_braces_in_flow_control_structures + # 在debug方法中引用所有的公共属性 + # - diagnostic_describe_all_properties + # 避免空的catch + - empty_catches + # 用;替代空的构造方法体 + - empty_constructor_bodies + # 避免使用空语句 + - empty_statements + # 当contains方法的参数和可枚举的类型不同,不要触发可枚举类型的contains方法 + - iterable_contains_unrelated_type + # 用小写加下划线做库的命名 + - library_prefixes + # 当remove方法的参数和List的类型不同,不要触发remove方法 + - list_remove_unrelated_type + # 不要出现只有一种情况的boolean表达式 + - literal_only_boolean_expressions + # 不要在list内用临近字符串方式 + - no_adjacent_strings_in_list + # 不要用重复值的case语句 + - no_duplicate_case_values + # 不要传null值给闭包语句 + - null_closures + # 省略对本地参数的类型声明 + - omit_local_variable_types + # 用相邻的string去减少string操作符 + - prefer_adjacent_string_concatenation + # 尽量用??=来判定null + - prefer_conditional_assignment + # 不要用indexOf来判定一个集合是否包含某元素 + - prefer_contains + # 用final来声明在类中不会被改变的私有参数 + - prefer_final_fields + # 当由iterables构建map时,用for语句 + - prefer_for_elements_to_map_fromIterable + # 用?? 替代null的检查 + - prefer_if_null_operators + # 用isNotEmpty替代!isEmpty + - prefer_is_not_empty + # 用iterable.whereType() 替代 iterable.where((e) => e is T). + - prefer_iterable_whereType + # 在lib路径下,建议使用相对路径引入 + - prefer_relative_imports + # 用单引号替代双引号 + - prefer_single_quotes + # 尽量用扩展符 + - prefer_spread_collections + # 在void可以的时候不要用null + - prefer_void_to_null + # 多余的getter + - recursive_getters + # 用/// 作为注释 + - slash_for_doc_comments + # 在重载==运算符时,判定入参的类型 + - test_types_in_equals + # 避免在finally语句中抛出错误 + - throw_in_finally + # 不要对已申明类型的变量在初始化时再声明 + - type_init_formals + # 在async的方法中await返回为future的方法 + - unawaited_futures + # 避免重复的const语句 + - unnecessary_const + # 避免在创建对象时用new + - unnecessary_new + # 避免在if null语句中用null + - unnecessary_null_in_if_null_operators + # 避免不必要的语句 + - unnecessary_statements + # 当不需要避免重复时不要用this + - unnecessary_this + # 避免无关类型的相等检查 + - unrelated_type_equality_checks + # 避免直接插入html + - unsafe_html + # 用类函数语法作为参数 + - use_function_type_syntax_for_parameters + # 用rethrow去重新抛出错误 + - use_rethrow_when_possible + # 用正确的正则去创建正则 + - valid_regexps diff --git a/example/android/.gitignore b/tdesign-component/example/android/.gitignore similarity index 100% rename from example/android/.gitignore rename to tdesign-component/example/android/.gitignore diff --git a/tdesign-component/example/android/app/build.gradle b/tdesign-component/example/android/app/build.gradle new file mode 100644 index 000000000..925c7d952 --- /dev/null +++ b/tdesign-component/example/android/app/build.gradle @@ -0,0 +1,80 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" // 替代旧插件加载 +} +apply from: "version.gradle" + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +//def flutterRoot = localProperties.getProperty('flutter.sdk') +//if (flutterRoot == null) { +// throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +//} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +//apply plugin: 'com.android.application' +//apply plugin: 'kotlin-android' +//apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +def keystoreProperties = new Properties() +def keystorePropertiesFile = rootProject.file('key.properties') +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} + +android { + + compileSdkVersion 34 + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.tdesign.tdesign_flutter_example" + minSdkVersion 21 + targetSdkVersion 31 + versionCode createVersionCode() + versionName createVersionName() + } + + signingConfigs { + release { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null + storePassword keystoreProperties['storePassword'] + } + } + + buildTypes { + release { + signingConfig signingConfigs.release + } + } +} + +flutter { + source '../..' +} + +//dependencies { +// implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +//} diff --git a/tdesign-component/example/android/app/src/debug/AndroidManifest.xml b/tdesign-component/example/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 000000000..676e9e9e6 --- /dev/null +++ b/tdesign-component/example/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/tdesign-component/example/android/app/src/main/AndroidManifest.xml b/tdesign-component/example/android/app/src/main/AndroidManifest.xml new file mode 100644 index 000000000..ecd86de6e --- /dev/null +++ b/tdesign-component/example/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example/android/app/src/main/kotlin/com/tdesign/tdesign_flutter_example/MainActivity.kt b/tdesign-component/example/android/app/src/main/kotlin/com/tdesign/tdesign_flutter_example/MainActivity.kt similarity index 100% rename from example/android/app/src/main/kotlin/com/tdesign/tdesign_flutter_example/MainActivity.kt rename to tdesign-component/example/android/app/src/main/kotlin/com/tdesign/tdesign_flutter_example/MainActivity.kt diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/tdesign-component/example/android/app/src/main/res/drawable-v21/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable-v21/launch_background.xml rename to tdesign-component/example/android/app/src/main/res/drawable-v21/launch_background.xml diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/tdesign-component/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable/launch_background.xml rename to tdesign-component/example/android/app/src/main/res/drawable/launch_background.xml diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/tdesign-component/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to tdesign-component/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/tdesign-component/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to tdesign-component/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/tdesign-component/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to tdesign-component/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/tdesign-component/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to tdesign-component/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/tdesign-component/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to tdesign-component/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/values-night/styles.xml b/tdesign-component/example/android/app/src/main/res/values-night/styles.xml similarity index 100% rename from example/android/app/src/main/res/values-night/styles.xml rename to tdesign-component/example/android/app/src/main/res/values-night/styles.xml diff --git a/example/android/app/src/main/res/values/styles.xml b/tdesign-component/example/android/app/src/main/res/values/styles.xml similarity index 100% rename from example/android/app/src/main/res/values/styles.xml rename to tdesign-component/example/android/app/src/main/res/values/styles.xml diff --git a/tdesign-component/example/android/app/src/profile/AndroidManifest.xml b/tdesign-component/example/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 000000000..676e9e9e6 --- /dev/null +++ b/tdesign-component/example/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + + + + diff --git a/tdesign-component/example/android/app/version.gradle b/tdesign-component/example/android/app/version.gradle new file mode 100644 index 000000000..c6413e703 --- /dev/null +++ b/tdesign-component/example/android/app/version.gradle @@ -0,0 +1,35 @@ +import java.text.SimpleDateFormat + +def getCfg() { + // 确保rootProject存在ext,并且ext中有config + if (rootProject.hasProperty('ext') && rootProject.ext.hasProperty('config')) { + return rootProject.ext.config + } + return null +} + +ext.createVersionCode = { -> + Date now = new Date() + SimpleDateFormat sf = new SimpleDateFormat("yyyyMMdd") + String nowStr = sf.format(now) + int nowInt = Integer.valueOf(nowStr) + // 本地环境 + nowInt * 100 + 1 +} + +ext.createVersionName = { -> + try { + // 定位文件:从当前目录(app)向上两级到example目录,再进入assets目录 + def versionFile = file("../../assets/version") + + // 检查文件是否存在 + if (!versionFile.exists()) { + throw new GradleException("❌ Version file not found at: ${versionFile.absolutePath}") + } + + // 读取文件内容并去除首尾空格 + return versionFile.text.trim() + } catch (Exception e) { + return "0.2.1.0" + } +} \ No newline at end of file diff --git a/tdesign-component/example/android/build.gradle b/tdesign-component/example/android/build.gradle new file mode 100644 index 000000000..669f3c632 --- /dev/null +++ b/tdesign-component/example/android/build.gradle @@ -0,0 +1,75 @@ +//buildscript { +// ext.kotlin_version = '1.7.10' +// +// ext { +// +// config = [ +// // 蓝盾自带变量 +// isCI : System.env.isCI // 是否在RMD的持续集成构建环境中 +// , VersionCode : System.env.VersionCode // VersionCode,每次发版本+2 +// , devopsUUID : System.env.uuid //蓝盾构建UUID +// , MajorVersion : System.env.MajorVersion // 主版本号 +// , MinorVersion : System.env.MinorVersion // 特性版本号 +// , FixVersion : System.env.FixVersion // 修正版本号 +// , BuildNo : System.env.BuildNo // 构建号 +// , isPublish : System.env.isPublish // 是否是外发版本,用来控制调试入口等 +// , isBeta : System.env.isBeta // 是否是灰度版本 +// , gitVersion : System.env.gitVersion // git提交hash +// , keyAlias : System.env.keyAlias // 签名信息 +// , storePassword: System.env.storePassword // storePassword +// , abi_filters: System.env.abi_filters // abi类型配置 +// ] +// +// def dir = System.getProperty("user.dir") +// def file = new File(dir, 'local.properties') +// Properties properties = new Properties() +// if (file.exists()) { +// properties.load(file.newDataInputStream()) +// } +// enableWatchman = Boolean.valueOf(properties.getProperty('enableWatchman', "")) +// } +// +// repositories { +// google() +// mavenCentral() +// } +// +// dependencies { +// classpath 'com.android.tools.build:gradle:7.0.1' +// classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" +// } +//} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} + +//buildscript { +// //待统一的kollin版本 +// ext.kotlin_version = '1.7.10' +// repositories { +// google() +// mavenCentral() +// } +// +// dependencies { +// //kotlin版本对应的gradle version +// classpath 'com.android.tools.build:gradle:7.1.2' +// classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" +// } +//} \ No newline at end of file diff --git a/example/android/gradle.properties b/tdesign-component/example/android/gradle.properties similarity index 78% rename from example/android/gradle.properties rename to tdesign-component/example/android/gradle.properties index 94adc3a3f..a6738207f 100644 --- a/example/android/gradle.properties +++ b/tdesign-component/example/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.enableR8=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/tdesign-component/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 93% rename from example/android/gradle/wrapper/gradle-wrapper.properties rename to tdesign-component/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58afd..02e5f5817 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/tdesign-component/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip diff --git a/tdesign-component/example/android/keystore b/tdesign-component/example/android/keystore new file mode 100644 index 000000000..54055ba5e Binary files /dev/null and b/tdesign-component/example/android/keystore differ diff --git a/tdesign-component/example/android/settings.gradle b/tdesign-component/example/android/settings.gradle new file mode 100644 index 000000000..fa3b8b140 --- /dev/null +++ b/tdesign-component/example/android/settings.gradle @@ -0,0 +1,27 @@ +pluginManagement { + // 动态获取 Flutter SDK 路径 + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + return properties.getProperty("flutter.sdk") + } + settings.ext.flutterSdkPath = flutterSdkPath() + + // 引入 Flutter 插件加载机制 + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + + apply from: "${rootDir}/app/version.gradle" // 加载 version.gradle 脚本 + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" // 声明式加载插件 + id "com.android.application" version "7.3.0" apply false // 根据实际 AGP 版本调整 + id "org.jetbrains.kotlin.android" version "1.9.22" apply false // Kotlin 版本需兼容 AGP +} + +include ":app" \ No newline at end of file diff --git a/tdesign-component/example/assets/api/action-sheet_api.md b/tdesign-component/example/assets/api/action-sheet_api.md new file mode 100644 index 000000000..f7936d0b7 --- /dev/null +++ b/tdesign-component/example/assets/api/action-sheet_api.md @@ -0,0 +1,54 @@ +## API +### TDActionSheet +#### 简介 +动作面板 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | context | 上下文 | +| align | TDActionSheetAlign | TDActionSheetAlign.center | 对齐方式 | +| cancelText | String | '取消' | 取消按钮的文本 | +| count | int | 8 | 每页显示的项目数 | +| rows | int | 2 | 显示的行数 | +| itemHeight | double | 96.0 | 项目的行高 | +| itemMinWidth | double | 80.0 | 项目的最小宽度 | +| description | String? | - | 描述文本 | +| items | List | - | ActionSheet的项目列表 | +| showCancel | bool | true | 是否显示取消按钮 | +| showPagination | bool | false | 是否显示分页 | +| scrollable | bool | false | 是否可以横向滚动 | +| theme | TDActionSheetTheme | TDActionSheetTheme.list | 主题样式 | +| visible | bool | false | 是否立即显示 | +| onCancel | VoidCallback? | - | 取消按钮的回调函数 | +| onClose | VoidCallback? | - | 关闭时的回调函数 | +| onSelected | TDActionSheetItemCallback? | - | 选择项目时的回调函数 | +| showOverlay | bool | true | 是否显示遮罩层 | +| closeOnOverlayClick | bool | true | 点击蒙层时是否关闭 | +| useSafeArea | bool | true | 使用安全区域 | + + +#### 静态方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| showListActionSheet | | required BuildContext context, required List items, TDActionSheetAlign align, String cancelText, bool showCancel, VoidCallback? onCancel, TDActionSheetItemCallback? onSelected, bool showOverlay, bool closeOnOverlayClick, VoidCallback? onClose, bool useSafeArea, | 显示列表类型面板 | +| showGridActionSheet | | required BuildContext context, required List items, TDActionSheetAlign align, String cancelText, bool showCancel, TDActionSheetItemCallback? onSelected, bool showOverlay, bool closeOnOverlayClick, int count, int rows, double itemHeight, double itemMinWidth, bool scrollable, bool showPagination, VoidCallback? onCancel, String? description, VoidCallback? onClose, bool useSafeArea, | 显示宫格类型面板 | +| showGroupActionSheet | | required BuildContext context, required List items, TDActionSheetAlign align, String cancelText, bool showCancel, TDActionSheetItemCallback? onSelected, bool showOverlay, bool closeOnOverlayClick, double itemHeight, double itemMinWidth, VoidCallback? onCancel, VoidCallback? onClose, bool useSafeArea, | 显示分组类型面板 | + +``` +``` + ### TDActionSheetItem +#### 简介 +动作面板项目 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| label | String | - | 标提 | +| textStyle | TextStyle? | - | 标题样式 | +| icon | Widget? | - | 图标 | +| badge | TDBadge? | - | 角标 | +| disabled | bool | false | 是否禁用 | +| iconSize | double? | - | 图标大小 | +| group | String? | - | 分组,用于带描述多行滚动宫格 | diff --git a/tdesign-component/example/assets/api/avatar_api.md b/tdesign-component/example/assets/api/avatar_api.md new file mode 100644 index 000000000..9d59a5ab3 --- /dev/null +++ b/tdesign-component/example/assets/api/avatar_api.md @@ -0,0 +1,24 @@ +## API +### TDAvatar +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| size | TDAvatarSize | TDAvatarSize.medium | 头像尺寸 | +| type | TDAvatarType | TDAvatarType.normal | 头像类型 | +| shape | TDAvatarShape | TDAvatarShape.circle | 头像形状 | +| text | String? | - | 自定义文字 | +| radius | double? | - | 自定义圆角 | +| icon | IconData? | - | 自定义图标 | +| avatarUrl | String? | - | 头像地址 | +| avatarSize | double? | - | 自定义头像大小 | +| avatarDisplayList | List? | - | 带操作\展示的头像列表 | +| displayText | String? | - | 纯展示类型末尾文字 | +| onTap | Function()? | - | 操作点击事件 | +| defaultUrl | String | '' | 默认图片(本地) | +| avatarDisplayWidget | Widget? | - | 带操作头像自定义操作Widget | +| avatarDisplayBorder | double | 2 | 带操作\展示的头像描边宽度 | +| avatarDisplayListAsset | List? | - | 带操作\展示的头像列表 (本地资源) | +| backgroundColor | Color? | - | 自定义文案时背景色 | +| fit | BoxFit? | - | 自定义图片对齐方式 | diff --git a/tdesign-component/example/assets/api/back-top_api.md b/tdesign-component/example/assets/api/back-top_api.md new file mode 100644 index 000000000..e21ae9001 --- /dev/null +++ b/tdesign-component/example/assets/api/back-top_api.md @@ -0,0 +1,12 @@ +## API +### TDBackTop +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| controller | ScrollController? | - | 页面滚动的控制器 | +| theme | TDBackTopTheme | TDBackTopTheme.light | 主题 | +| style | TDBackTopStyle | TDBackTopStyle.circle | 样式,圆形和半圆 | +| showText | bool | false | 是否展示文字 | +| onClick | VoidCallback? | - | 按钮点击事件 | diff --git a/tdesign-component/example/assets/api/badge_api.md b/tdesign-component/example/assets/api/badge_api.md new file mode 100644 index 000000000..3dc06639d --- /dev/null +++ b/tdesign-component/example/assets/api/badge_api.md @@ -0,0 +1,19 @@ +## API +### TDBadge +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| type | TDBadgeType | type | 红点样式 | +| key | | - | | +| count | String? | - | 红点数量 | +| maxCount | String? | '99' | 最大红点数量 | +| border | TDBadgeBorder | TDBadgeBorder.large | 红点圆角大小 | +| size | TDBadgeSize | TDBadgeSize.small | 红点尺寸 | +| color | Color? | - | 红点颜色 | +| textColor | Color? | - | 文字颜色 | +| message | String? | - | 消息内容 | +| widthLarge | double | 32 | 角标大三角形宽 | +| widthSmall | double | 12 | 角标小三角形宽 | +| padding | EdgeInsetsGeometry? | - | 角标自定义padding | +| showZero | bool | true | 值为0是否显示 | diff --git a/tdesign-component/example/assets/api/button_api.md b/tdesign-component/example/assets/api/button_api.md new file mode 100644 index 000000000..9260840ad --- /dev/null +++ b/tdesign-component/example/assets/api/button_api.md @@ -0,0 +1,53 @@ +## API +### TDButtonStyle +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| backgroundColor | Color? | - | 背景颜色 | +| frameColor | Color? | - | 边框颜色 | +| textColor | Color? | - | 文字颜色 | +| frameWidth | double? | - | 边框宽度 | +| radius | BorderRadiusGeometry? | - | 自定义圆角 | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDButtonStyle.generateFillStyleByTheme | 生成不同主题的填充按钮样式 | +| TDButtonStyle.generateOutlineStyleByTheme | 生成不同主题的描边按钮样式 | +| TDButtonStyle.generateTextStyleByTheme | 生成不同主题的文本按钮样式 | +| TDButtonStyle.generateGhostStyleByTheme | 生成不同主题的幽灵按钮样式 | + +``` +``` + ### TDButton +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| text | String? | - | 文本内容 | +| size | TDButtonSize | TDButtonSize.medium | 尺寸 | +| type | TDButtonType | TDButtonType.fill | 类型:填充,描边,文字 | +| shape | TDButtonShape | TDButtonShape.rectangle | 形状:圆角,胶囊,方形,圆形,填充 | +| theme | TDButtonTheme? | - | 主题 | +| child | Widget? | - | 自控件 | +| disabled | bool | false | 禁止点击 | +| isBlock | bool | false | 是否为通栏按钮 | +| style | TDButtonStyle? | - | 自定义样式,有则优先用它,没有则根据type和theme选取.如果设置了style,则activeStyle和disableStyle也应该设置 | +| activeStyle | TDButtonStyle? | - | 自定义点击样式,有则优先用它,没有则根据type和theme选取 | +| disableStyle | TDButtonStyle? | - | 自定义禁用样式,有则优先用它,没有则根据type和theme选取 | +| textStyle | TextStyle? | - | 自定义可点击状态文本样式 | +| disableTextStyle | TextStyle? | - | 自定义不可点击状态文本样式 | +| width | double? | - | 自定义宽度 | +| height | double? | - | 自定义高度 | +| onTap | TDButtonEvent? | - | 点击事件 | +| icon | IconData? | - | 图标icon | +| iconWidget | Widget? | - | 自定义图标icon控件 | +| iconTextSpacing | double? | - | 自定义图标与文本之间距离 | +| onLongPress | TDButtonEvent? | - | 长按事件 | +| margin | EdgeInsetsGeometry? | - | 自定义margin | +| padding | EdgeInsetsGeometry? | - | 自定义padding | +| iconPosition | TDButtonIconPosition? | TDButtonIconPosition.left | 图标位置 | diff --git a/tdesign-component/example/assets/api/calendar_api.md b/tdesign-component/example/assets/api/calendar_api.md new file mode 100644 index 000000000..14ebff988 --- /dev/null +++ b/tdesign-component/example/assets/api/calendar_api.md @@ -0,0 +1,77 @@ +## API +### TDCalendarPopup +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | context | 上下文 | +| top | double? | - | 距离顶部的距离 | +| autoClose | bool? | true | 自动关闭;在点击关闭按钮、确认按钮、遮罩层时自动关闭 | +| confirmBtn | Widget? | - | 自定义确认按钮 | +| visible | bool? | - | 默认是否显示日历 | +| onClose | VoidCallback? | - | 关闭时触发 | +| onConfirm | void Function(List value)? | - | 点击确认按钮时触发 | +| builder | CalendarBuilder? | - | 控件构建器,优先级高于[child] | +| child | TDCalendar? | - | 日历控件 | + +``` +``` + ### TDCalendarStyle +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| decoration | | - | | +| titleStyle | TextStyle? | - | header区域 [TDCalendar.title]的样式 | +| titleMaxLine | int? | - | header区域 [TDCalendar.title]的行数 | +| titleCloseColor | Color? | - | header区域 关闭图标的颜色 | +| weekdayStyle | TextStyle? | - | header区域 周 文字样式 | +| monthTitleStyle | TextStyle? | - | body区域 年月文字样式 | +| cellStyle | TextStyle? | - | 日期样式 | +| centreColor | Color? | - | 日期范围内背景样式 | +| cellDecoration | BoxDecoration? | - | 日期decoration | +| cellPrefixStyle | TextStyle? | - | 日期前面的字符串的样式 | +| cellSuffixStyle | TextStyle? | - | 日期后面的字符串的样式 | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDCalendarStyle.generateStyle | 生成默认样式 | +| TDCalendarStyle.cellStyle | 日期样式 | + +``` +``` + ### TDCalendar +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| firstDayOfWeek | int? | 0 | 第一天从星期几开始,默认 0 = 周日 | +| format | CalendarFormat? | - | 用于格式化日期的函数,可定义日期前后的显示内容和日期样式 | +| maxDate | int? | - | 最大可选的日期(fromMillisecondsSinceEpoch),不传则默认半年后 | +| minDate | int? | - | 最小可选的日期(fromMillisecondsSinceEpoch),不传则默认今天 | +| title | String? | - | 标题 | +| titleWidget | Widget? | - | 标题组件 | +| type | CalendarType? | CalendarType.single | 日历的选择类型,single = 单选;multiple = 多选; range = 区间选择 | +| value | List? | - | 当前选择的日期(fromMillisecondsSinceEpoch),不传则默认今天,当 type = single 时数组长度为1 | +| displayFormat | String? | 'year month' | 年月显示格式,`year`表示年,`month`表示月,如`year month`表示年在前、月在后、中间隔一个空格 | +| cellHeight | double? | 60 | 日期高度 | +| height | double? | - | 高度 | +| width | double? | - | 宽度 | +| style | TDCalendarStyle? | - | 自定义样式 | +| onChange | void Function(List value)? | - | 选中值变化时触发 | +| onCellClick | void Function(int value, DateSelectType type, TDate tdate)? | - | 点击日期时触发 | +| onCellLongPress | void Function(int value, DateSelectType type, TDate tdate)? | - | 长安日期时触发 | +| onHeaderClick | void Function(int index, String week)? | - | 点击周时触发 | +| useTimePicker | bool? | false | 是否显示时间选择器 | +| timePickerModel | List? | - | 自定义时间选择器 | +| monthTitleHeight | double? | 22 | 月标题高度 | +| monthTitleBuilder | Widget Function(BuildContext context, DateTime monthDate)? | - | 月标题构建器 | +| pickerHeight | double? | 178 | 时间选择器List的视窗高度 | +| pickerItemCount | int? | 3 | 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 | +| isTimeUnit | bool? | true | 是否显示时间单位 | +| animateTo | bool? | false | 动画滚动到指定位置 | +| cellWidget | Widget? Function(BuildContext context, TDate tdate, DateSelectType selectType)? | - | 自定义日期单元格组件 | diff --git a/tdesign-component/example/assets/api/cascader_api.md b/tdesign-component/example/assets/api/cascader_api.md new file mode 100644 index 000000000..7751aa322 --- /dev/null +++ b/tdesign-component/example/assets/api/cascader_api.md @@ -0,0 +1,22 @@ +## API +### TDMultiCascader +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| title | String? | - | 选择器标题 | +| titleStyle | TextStyle? | - | 标题样式 | +| theme | String? | - | 展示风格 可选项:step/tab | +| subTitles | List? | - | 每级展示的次标题 | +| data | List | - | 选择器的数据源 | +| cascaderHeight | double | - | 选择器List的视窗高度,默认200 | +| initialIndexes | List? | - | 若为null表示全部从零开始 | +| initialData | String? | - | 初始化数据 | +| backgroundColor | Color? | - | 背景颜色 | +| topRadius | double? | - | 顶部圆角 | +| closeText | String? | - | 关闭按钮文本 | +| isLetterSort | bool | false | 是否开启字母排序 | +| onClose | Function? | - | 选择器关闭按钮回调 | +| action | TDCascaderAction? | - | 自定义选择器右上角按钮 | +| onChange | MultiCascaderCallback | - | 值发生变更时触发 | diff --git a/tdesign-component/example/assets/api/cell_api.md b/tdesign-component/example/assets/api/cell_api.md new file mode 100644 index 000000000..407c65d40 --- /dev/null +++ b/tdesign-component/example/assets/api/cell_api.md @@ -0,0 +1,89 @@ +## API +### TDCellGroup +#### 简介 +单元格组组件 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| bordered | bool? | false | 是否显示组边框 | +| theme | TDCellGroupTheme? | TDCellGroupTheme.defaultTheme | 单元格组风格。可选项:default/card | +| title | String? | - | 单元格组标题 | +| cells | List | - | 单元格列表 | +| builder | CellBuilder? | - | cell构建器,可自定义cell父组件,如Dismissible | +| style | TDCellStyle? | - | 自定义样式 | +| titleWidget | Widget? | - | 单元格组标题组件 | +| scrollable | bool? | false | 可滚动 | +| isShowLastBordered | bool? | false | 是否显示最后一个cell的下边框 | + +``` +``` + ### TDCellStyle +#### 简介 +单元格组件样式 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext? | - | 传递context,会生成默认样式 | +| leftIconColor | Color? | - | 左侧图标颜色 | +| rightIconColor | Color? | - | 右侧图标颜色 | +| titleStyle | TextStyle? | - | 标题文字样式 | +| requiredStyle | TextStyle? | - | 必填星号文字样式 | +| descriptionStyle | TextStyle? | - | 内容描述文字样式 | +| noteStyle | TextStyle? | - | 说明文字样式 | +| arrowColor | Color? | - | 箭头颜色 | +| borderedColor | Color? | - | 单元格边框颜色 | +| groupBorderedColor | Color? | - | 单元格组边框颜色 | +| backgroundColor | Color? | - | 默认状态背景颜色 | +| clickBackgroundColor | Color? | - | 点击状态背景颜色 | +| groupTitleStyle | TextStyle? | - | 单元组标题文字样式 | +| padding | EdgeInsets? | - | 单元格内边距 | +| cardBorderRadius | BorderRadius? | - | 卡片模式边框圆角 | +| cardPadding | EdgeInsets? | - | 卡片模式内边距 | +| titlePadding | EdgeInsets? | - | 单元格组标题内边距 | +| titleBackgroundColor | Color? | - | 单元格组标题背景颜色 | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDCellStyle.cellStyle | 生成单元格默认样式 | + +``` +``` + ### TDCell +#### 简介 +单元格组件 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| align | TDCellAlign? | TDCellAlign.middle | 内容的对齐方式,默认居中对齐。可选项:top/middle/bottom | +| arrow | bool? | false | 是否显示右侧箭头 | +| bordered | bool? | true | 是否显示下边框,仅在TDCellGroup组件下起作用 | +| description | String? | - | 下方内容描述文字 | +| descriptionWidget | Widget? | - | 下方内容描述组件 | +| hover | bool? | true | 是否开启点击反馈 | +| image | ImageProvider? | - | 主图 | +| imageSize | double? | - | 主图尺寸 | +| imageWidget | Widget? | - | 主图组件 | +| leftIcon | IconData? | - | 左侧图标,出现在单元格标题的左侧 | +| leftIconWidget | Widget? | - | 左侧图标组件 | +| note | String? | - | 和标题同行的说明文字 | +| noteWidget | Widget? | - | 说明文字组件 | +| required | bool? | false | 是否显示表单必填星号 | +| title | String? | - | 标题 | +| titleWidget | Widget? | - | 标题组件 | +| onClick | TDCellClick? | - | 点击事件 | +| onLongPress | TDCellClick? | - | 长按事件 | +| style | TDCellStyle? | - | 自定义样式 | +| rightIcon | IconData? | - | 最右侧图标 | +| rightIconWidget | Widget? | - | 最右侧图标组件 | +| disabled | bool? | false | 禁用 | +| imageCircle | double? | 50 | 主图圆角,默认50(圆形) | +| showBottomBorder | bool? | false | 是否显示下边框(建议TDCellGroup组件下false,避免与bordered重叠) | +| height | double? | - | 高度 | diff --git a/tdesign-component/example/assets/api/checkbox_api.md b/tdesign-component/example/assets/api/checkbox_api.md new file mode 100644 index 000000000..3607f322d --- /dev/null +++ b/tdesign-component/example/assets/api/checkbox_api.md @@ -0,0 +1,54 @@ +## API +### TDCheckbox +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| id | String? | - | id | +| key | | - | | +| title | String? | - | 文本 | +| subTitle | String? | - | 辅助文字 | +| titleFont | Font? | - | 标题字体大小 | +| subTitleFont | Font? | - | 副标题字体大小 | +| enable | bool | true | 不可用 | +| checked | bool | false | 选中状态。默认为`false` | +| titleMaxLine | int? | - | 标题的行数 | +| subTitleMaxLine | int? | 1 | 辅助文字的行数 | +| customIconBuilder | IconBuilder? | - | 自定义Checkbox显示样式 | +| customContentBuilder | ContentBuilder? | - | 完全自定义内容 | +| insetSpacing | double? | 16 | 文字和非图标侧的距离 | +| style | TDCheckboxStyle? | - | 复选框样式:圆形或方形 | +| spacing | double? | - | icon和文字的距离 | +| backgroundColor | Color? | - | 背景颜色 | +| selectColor | Color? | - | 选择颜色 | +| disableColor | Color? | - | 禁用选择颜色 | +| size | TDCheckBoxSize | TDCheckBoxSize.small | 复选框大小 | +| cardMode | bool | false | 展示为卡片模式 | +| showDivider | bool | true | 是否展示分割线 | +| contentDirection | TDContentDirection | TDContentDirection.right | 文字相对icon的方位 | +| onCheckBoxChanged | OnCheckValueChanged? | - | 切换监听 | +| titleColor | Color? | - | 标题文字颜色 | +| subTitleColor | Color? | - | 副标题文字颜色 | +| checkBoxLeftSpace | double? | - | 选项框左侧间距 | +| customSpace | EdgeInsetsGeometry? | - | 自定义组件间距 | + +``` +``` + ### TDCheckboxGroup +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| child | | - | | +| key | | - | | +| onChangeGroup | OnGroupChange? | - | 状态变化监听器 | +| controller | TDCheckboxGroupController? | - | 可以通过控制器操作勾选状态 | +| checkedIds | List? | - | 勾选的CheckBox id列表 | +| maxChecked | int? | - | 最多可以勾选多少 | +| titleMaxLine | int? | - | CheckBox标题的行数 | +| customContentBuilder | ContentBuilder? | - | CheckBox完全自定义内容 | +| contentDirection | TDContentDirection? | - | 文字相对icon的方位 | +| style | TDCheckboxStyle? | - | CheckBox复选框样式:圆形或方形 | +| spacing | double? | - | CheckBoxicon和文字的距离 | +| customIconBuilder | IconBuilder? | - | 自定义选择icon的样式 | +| onOverloadChecked | VoidCallback? | - | 超过最大可勾选的个数 | diff --git a/tdesign-component/example/assets/api/collapse_api.md b/tdesign-component/example/assets/api/collapse_api.md new file mode 100644 index 000000000..50d50ebdd --- /dev/null +++ b/tdesign-component/example/assets/api/collapse_api.md @@ -0,0 +1,21 @@ +## API +### TDCollapse +#### 简介 +折叠面板列表组件,需配合 [TDCollapsePanel] 使用 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| children | List | - | 折叠面板列表的子组件 | +| style | TDCollapseStyle | TDCollapseStyle.block | 折叠面板列表的样式 | +| expansionCallback | ExpansionPanelCallback? | - | 折叠面板列表的回调函数; | +| animationDuration | Duration | kThemeAnimationDuration | 折叠面板列表的动画时长 | +| elevation | double | 0 | 折叠面板列表的阴影 | +| key | | - | | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDCollapse.accordion | | diff --git a/tdesign-component/example/assets/api/date-time-picker_api.md b/tdesign-component/example/assets/api/date-time-picker_api.md new file mode 100644 index 000000000..562bd09d2 --- /dev/null +++ b/tdesign-component/example/assets/api/date-time-picker_api.md @@ -0,0 +1,43 @@ +## API +### TDDatePicker +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| title | String | - | 选择器标题 | +| onConfirm | DatePickerCallback? | - | 选择器确认按钮回调 | +| rightText | String? | - | 右侧按钮文案 | +| leftText | String? | - | 左侧按钮文案 | +| onCancel | DatePickerCallback? | - | 选择器取消按钮回调 | +| backgroundColor | Color? | - | 背景颜色 | +| titleDividerColor | Color? | - | 标题分割线颜色 | +| topRadius | double? | - | 顶部圆角 | +| titleHeight | double? | - | 标题高度 | +| padding | EdgeInsets? | - | 适配padding | +| leftPadding | double? | - | 左边填充 | +| rightPadding | double? | - | 右边填充 | +| leftTextStyle | TextStyle? | - | 自定义左侧文案样式 | +| rightTextStyle | TextStyle? | - | 自定义右侧文案样式 | +| centerTextStyle | TextStyle? | - | 自定义中间文案样式 | +| customSelectWidget | Widget? | - | 自定义选择框样式 | +| itemDistanceCalculator | ItemDistanceCalculator? | - | 根据距离计算字体颜色、透明度、粗细 | +| model | DatePickerModel | - | 数据模型 | +| showTitle | bool | true | 是否展示标题 | +| pickerHeight | double | 200 | 选择器List的视窗高度,默认200 | +| pickerItemCount | int | - | 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 | +| isTimeUnit | bool? | - | 是否时间显示 | +| onSelectedItemChanged | void Function(int wheelIndex, int index)? | - | 选择器选中项改变回调 | +| itemBuilder | ItemBuilderType? | - | 自定义item构建 | +| key | | - | | + +``` +``` + ### TDPicker + +#### 静态方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| showDatePicker | | required null context, required String title, required DatePickerCallback? onConfirm, DatePickerCallback? onCancel, bool useYear, bool useMonth, bool useDay, bool useHour, bool useMinute, bool useSecond, bool useWeekDay, Color? barrierColor, List dateStart, List? dateEnd, List? initialDate, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, Color? titleDividerColor, Widget? customSelectWidget, Duration duration, double pickerHeight, bool isTimeUnit, Function(int wheelIndex, int index)? onSelectedItemChanged, int pickerItemCount, List Function(DateTypeKey key, List nums)? filterItems, ItemBuilderType? itemBuilder, | 显示时间选择器 | +| showMultiPicker | | required null context, String? title, required MultiPickerCallback? onConfirm, MultiPickerCallback? onCancel, required List> data, List? initialIndexes, Duration duration, Color? barrierColor, double pickerHeight, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, Color? titleDividerColor, double? topPadding, int pickerItemCount, Widget? customSelectWidget, ItemBuilderType? itemBuilder, | 显示多级选择器 | +| showMultiLinkedPicker | | required null context, String? title, required MultiPickerCallback? onConfirm, MultiPickerCallback? onCancel, required Map data, required int columnNum, required List initialData, Duration duration, Color? barrierColor, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, double pickerHeight, Color? titleDividerColor, Widget? customSelectWidget, double? topPadding, bool keepSameSelection, int pickerItemCount, | 显示多级联动选择器 | diff --git a/tdesign-component/example/assets/api/dialog_api.md b/tdesign-component/example/assets/api/dialog_api.md new file mode 100644 index 000000000..985932e7f --- /dev/null +++ b/tdesign-component/example/assets/api/dialog_api.md @@ -0,0 +1,218 @@ +## API +### TDAlertDialog +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| backgroundColor | Color | Colors.white | 背景颜色 | +| radius | double | 12.0 | 圆角 | +| title | String? | - | 标题 | +| titleColor | Color | const Color(0xE6000000) | 标题颜色 | +| content | String? | - | 内容 | +| contentColor | Color? | - | 内容颜色 | +| titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | +| contentWidget | Widget? | - | 内容Widget | +| contentMaxHeight | double | 0 | 内容的最大高度,默认为0,也就是不限制高度 | +| leftBtn | TDDialogButtonOptions? | - | 左侧按钮配置 | +| rightBtn | TDDialogButtonOptions? | - | 右侧按钮配置 | +| leftBtnAction | Function()? | - | 左侧按钮默认点击 | +| rightBtnAction | Function()? | - | 右侧按钮默认点击 | +| showCloseButton | bool? | - | 显示右上角关闭按钮 | +| buttonStyle | | TDDialogButtonStyle.normal | | +| padding | EdgeInsets? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容内边距 | +| buttonWidget | Widget? | - | 自定义按钮 | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDAlertDialog.vertical | 纵向按钮排列的对话框 + + [buttons]参数是必须的,纵向按钮默认样式都是[TDButtonTheme.primary] | + +``` +``` + ### TDImageDialog +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| image | Image | - | 图片 | +| imagePosition | TDDialogImagePosition? | TDDialogImagePosition.top | 图片位置 | +| backgroundColor | Color | Colors.white | 背景颜色 | +| radius | double | 12.0 | 圆角 | +| title | String? | - | 标题 | +| titleColor | Color | const Color(0xE6000000) | 标题颜色 | +| titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | +| contentWidget | Widget? | - | 内容Widget | +| content | String? | - | 内容 | +| contentColor | Color? | - | 内容颜色 | +| leftBtn | TDDialogButtonOptions? | - | 左侧按钮配置 | +| rightBtn | TDDialogButtonOptions? | - | 右侧按钮配置 | +| showCloseButton | bool? | - | 显示右上角关闭按钮 | +| padding | EdgeInsets? | - | 内容内边距 | +| buttonWidget | Widget? | - | 自定义按钮 | + +``` +``` + ### TDInputDialog +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| textEditingController | TextEditingController | - | 输入controller | +| backgroundColor | Color | Colors.white | 背景颜色 | +| radius | double | 12.0 | 圆角 | +| title | String? | - | 标题 | +| titleColor | Color | const Color(0xE6000000) | 标题颜色 | +| titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | +| contentWidget | Widget? | - | 内容Widget | +| content | String? | - | 内容 | +| hintText | String? | '' | 输入提示 | +| contentColor | Color? | - | 内容颜色 | +| leftBtn | TDDialogButtonOptions? | - | 左侧按钮配置 | +| rightBtn | TDDialogButtonOptions? | - | 右侧按钮配置 | +| showCloseButton | bool? | - | 显示右上角关闭按钮 | +| padding | EdgeInsets? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容内边距 | +| buttonWidget | Widget? | - | 自定义按钮 | +| customInputWidget | Widget? | - | 自定义输入框 | + +``` +``` + ### TDDialogScaffold +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| body | Widget | - | Dialog主体 | +| showCloseButton | bool? | - | 显示右上角关闭按钮 | +| backgroundColor | Color | Colors.white | 背景色 | +| radius | double | 12.0 | 圆角 | + +``` +``` + ### TDDialogTitle +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| title | String? | - | 标题文字 | +| titleColor | Color | Colors.black | 标题颜色 | + +``` +``` + ### TDDialogContent +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| content | String? | - | 标题文字 | +| contentColor | Color | const Color(0x99000000) | 标题颜色 | + +``` +``` + ### TDDialogInfoWidget +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| title | String? | - | 标题 | +| titleColor | Color | Colors.black | 标题颜色 | +| titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | +| contentWidget | Widget? | - | 内容Widget | +| content | String? | - | 内容 | +| contentColor | Color? | - | 内容颜色 | +| contentMaxHeight | double | 0 | 内容的最大高度,默认为0,也就是不限制高度 | +| padding | EdgeInsetsGeometry? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容的内边距 | + +``` +``` + ### HorizontalNormalButtons +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| leftBtn | TDDialogButtonOptions | - | 左按钮 | +| rightBtn | TDDialogButtonOptions | - | 右按钮 | + +``` +``` + ### HorizontalTextButtons +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| leftBtn | TDDialogButtonOptions | - | 左按钮 | +| rightBtn | TDDialogButtonOptions | - | 右按钮 | + +``` +``` + ### TDDialogButton +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| buttonText | String? | - | 按钮文字 | +| buttonTextColor | Color? | - | 按钮文字颜色 | +| buttonTextSize | double? | - | 按钮文字大小 | +| buttonTextFontWeight | FontWeight? | FontWeight.w600 | 按钮文字粗细 | +| buttonStyle | TDButtonStyle? | - | 按钮样式 | +| buttonType | TDButtonType? | - | 按钮类型 | +| buttonTheme | TDButtonTheme? | - | 按钮主题 | +| onPressed | Function() | - | 点击 | +| height | double? | 40.0 | 按钮高度 | +| width | double? | - | 按钮宽度 | +| isBlock | bool | true | 按钮高度 | + +``` +``` + ### TDConfirmDialog +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| action | Function()? | - | 点击 | +| backgroundColor | Color | Colors.white | 背景颜色 | +| radius | double | 12.0 | 圆角 | +| title | String? | - | 标题 | +| titleColor | Color | const Color(0xE6000000) | 标题颜色 | +| titleAlignment | AlignmentGeometry? | - | 标题对齐模式 | +| contentWidget | Widget? | - | 内容Widget | +| content | String? | - | 内容 | +| contentColor | Color? | - | 内容颜色 | +| contentMaxHeight | double | 0 | 内容的最大高度,默认为0,也就是不限制高度 | +| buttonText | String? | - | 按钮文字 | +| buttonTextColor | Color? | - | 按钮文字颜色 | +| buttonStyle | TDDialogButtonStyle | TDDialogButtonStyle.normal | 按钮样式 | +| showCloseButton | bool? | - | 右上角关闭按钮 | +| padding | EdgeInsets? | const EdgeInsets.fromLTRB(24, 32, 24, 0) | 内容内边距 | +| buttonWidget | Widget? | - | 自定义按钮 | + +``` +``` + ### TDDialogButtonOptions +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| title | String | - | 标题内容 | +| action | Function()? | - | 点击操作 | +| titleColor | Color? | - | 标题颜色 | +| titleSize | double? | - | 字体大小 | +| style | TDButtonStyle? | - | 按钮样式 | +| type | TDButtonType? | - | 按钮类型 | +| theme | TDButtonTheme? | - | 按钮类型 | +| height | double? | - | 按钮高度 | +| fontWeight | FontWeight? | - | 字体粗细 | diff --git a/example/assets/api/divider_api.md b/tdesign-component/example/assets/api/divider_api.md similarity index 78% rename from example/assets/api/divider_api.md rename to tdesign-component/example/assets/api/divider_api.md index cdddfbc8d..95d30d7f0 100644 --- a/example/assets/api/divider_api.md +++ b/tdesign-component/example/assets/api/divider_api.md @@ -1,15 +1,19 @@ ## API +### TDDivider +#### 默认构造方法 | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | -| key | Key | - | | +| key | | - | | | color | Color? | - | 线条颜色 | | margin | EdgeInsetsGeometry? | - | 外部填充 | | width | double? | - | 宽度,需要竖向线条时使用 | | height | double? | - | 高度,横向线条使用 | | text | String? | - | 文本字符串,使用默认样式 | +| textStyle | TextStyle? | - | 自定义文本样式 | | widget | Widget? | - | 中间控件,可自定义样式 | | gapPadding | EdgeInsetsGeometry? | - | 线条和中间文本之间的填充 | | hideLine | bool | false | 隐藏线条,使用纯文本分割 | | isDashed | bool | false | 是否为虚线 | +| alignment | TextAlignment | TextAlignment.center | 文字位置 | | direction | Axis | Axis.horizontal | 方向,竖直虚线必须传 | diff --git a/tdesign-component/example/assets/api/drawer_api.md b/tdesign-component/example/assets/api/drawer_api.md new file mode 100644 index 000000000..f700a61a6 --- /dev/null +++ b/tdesign-component/example/assets/api/drawer_api.md @@ -0,0 +1,64 @@ +## API +### TDDrawerWidget +#### 简介 +抽屉内容组件 + 可用于Scaffold中的drawer属性 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| footer | Widget? | - | 抽屉的底部 | +| items | List? | - | 抽屉里的列表项 | +| contentWidget | Widget? | - | 自定义内容,优先级高于[items]/[footer]/[title] | +| title | String? | - | 抽屉的标题 | +| titleWidget | Widget? | - | 抽屉的标题组件 | +| onItemClick | TDDrawerItemClickCallback? | - | 点击抽屉里的列表项触发 | +| width | double? | 280 | 宽度 | +| style | TDCellStyle? | - | 列表自定义样式 | +| hover | bool? | true | 是否开启点击反馈 | +| backgroundColor | Color? | - | 组件背景颜色 | +| bordered | bool? | true | 是否显示边框 | +| isShowLastBordered | bool? | true | 是否显示最后一行分割线 | + +``` +``` + ### TDDrawerItem +#### 简介 +抽屉里的列表项 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| title | String? | - | 每列标题 | +| icon | Widget? | - | 每列图标 | +| content | Widget? | - | 完全自定义 | + +``` +``` + ### TDDrawer +#### 简介 +抽屉组件 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext | context | 上下文 | +| closeOnOverlayClick | bool? | true | 点击蒙层时是否关闭抽屉 | +| footer | Widget? | - | 抽屉的底部 | +| items | List? | - | 抽屉里的列表项 | +| placement | TDDrawerPlacement? | TDDrawerPlacement.right | 抽屉方向 | +| showOverlay | bool? | true | 是否显示遮罩层 | +| title | String? | - | 抽屉的标题 | +| titleWidget | Widget? | - | 抽屉的标题组件 | +| visible | bool? | - | 组件是否可见 | +| onClose | VoidCallback? | - | 关闭时触发 | +| onItemClick | TDDrawerItemClickCallback? | - | 点击抽屉里的列表项触发 | +| width | double? | 280 | 宽度 | +| drawerTop | double? | - | 距离顶部的距离 | +| style | TDCellStyle? | - | 列表自定义样式 | +| hover | bool? | true | 是否开启点击反馈 | +| backgroundColor | Color? | - | 组件背景颜色 | +| bordered | bool? | true | 是否显示边框 | +| isShowLastBordered | bool? | true | 是否显示最后一行分割线 | +| contentWidget | Widget? | - | 自定义内容,优先级高于[items]/[footer]/[title] | diff --git a/tdesign-component/example/assets/api/dropdown-menu_api.md b/tdesign-component/example/assets/api/dropdown-menu_api.md new file mode 100644 index 000000000..b9bae6a61 --- /dev/null +++ b/tdesign-component/example/assets/api/dropdown-menu_api.md @@ -0,0 +1,67 @@ +## API +### TDDropdownMenu +#### 简介 +下拉菜单 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| builder | TDDropdownItemBuilder? | - | 下拉菜单构建器,优先级高于[items] | +| items | List? | - | 下拉菜单 | +| closeOnClickOverlay | bool? | true | 是否在点击遮罩层后关闭菜单 | +| direction | TDDropdownMenuDirection? | TDDropdownMenuDirection.auto | 菜单展开方向(down、up、auto) | +| duration | double? | 200.0 | 动画时长,毫秒 | +| showOverlay | bool? | true | 是否显示遮罩层 | +| isScrollable | bool? | false | 是否开启滚动列表 | +| arrowIcon | IconData? | - | 自定义箭头图标 | +| labelBuilder | LabelBuilder? | - | 自定义标签内容 | +| onMenuOpened | ValueChanged? | - | 展开菜单事件 | +| onMenuClosed | ValueChanged? | - | 关闭菜单事件 | +| width | double? | - | menu的宽度 | +| height | double? | 48 | menu的高度 | +| tabBarAlign | MainAxisAlignment? | MainAxisAlignment.center | [TDDropdownItem.label]和[arrowIcon]/[TDDropdownItem.arrowIcon]的对齐方式 | +| decoration | Decoration? | - | 下拉菜单的装饰器 | + +``` +``` + ### TDDropdownItem +#### 简介 +下拉菜单内容 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| disabled | bool? | false | 是否禁用 | +| label | String? | - | 标题 | +| arrowIcon | IconData? | - | 自定义箭头图标 | +| multiple | bool? | false | 是否多选 | +| options | List? | const [] | 选项数据 | +| builder | TDDropdownItemContentBuilder? | - | 完全自定义展示内容 | +| optionsColumns | int? | 1 | 选项分栏(1-3) | +| onChange | ValueChanged? | - | 值改变时触发 | +| onConfirm | ValueChanged? | - | 点击确认时触发 | +| onReset | VoidCallback? | - | 点击重置时触发 | +| minHeight | double? | - | 内容最小高度 | +| maxHeight | double? | - | 内容最大高度 | +| tabBarWidth | double? | - | 该item在menu上的宽度,仅在[TDDropdownMenu.isScrollable]为true时有效 | +| tabBarAlign | MainAxisAlignment? | - | [label]和[arrowIcon]/[TDDropdownMenu.arrowIcon]的对齐方式 | +| tabBarFlex | int? | 1 | 该item在menu上的宽度占比,仅在[TDDropdownMenu.isScrollable]为false时有效 | + +``` +``` + ### TDDropdownItemOption +#### 简介 +选项数据 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| value | String | - | 选项值 | +| label | String | - | 选项标题 | +| disabled | bool? | false | 是否禁用 | +| group | String? | - | 分组,相同的为一组 | +| selected | bool? | false | 是否选中 | +| selectedColor | Color? | - | 选中颜色 | +| disabledColor | Color? | - | 禁用颜色 | diff --git a/tdesign-component/example/assets/api/empty_api.md b/tdesign-component/example/assets/api/empty_api.md new file mode 100644 index 000000000..daad02a3d --- /dev/null +++ b/tdesign-component/example/assets/api/empty_api.md @@ -0,0 +1,16 @@ +## API +### TDEmpty +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| type | TDEmptyType | TDEmptyType.plain | 类型,为operation有操作按钮,plain无按钮 | +| image | Widget? | - | 展示图片 | +| emptyText | String? | - | 描述文字 | +| operationText | String? | - | 操作按钮文案 | +| operationTheme | TDButtonTheme? | - | 操作按钮文案主题色 | +| onTapEvent | TDTapEvent? | - | 点击事件 | +| emptyTextColor | Color? | - | 描述文字颜色 | +| emptyTextFont | Font? | - | 描述文字大小 | +| customOperationWidget | Widget? | - | 自定义操作按钮 | +| key | | - | | diff --git a/tdesign-component/example/assets/api/fab_api.md b/tdesign-component/example/assets/api/fab_api.md new file mode 100644 index 000000000..eb62a5a22 --- /dev/null +++ b/tdesign-component/example/assets/api/fab_api.md @@ -0,0 +1,13 @@ +## API +### TDFab +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| theme | TDFabTheme | TDFabTheme.defaultTheme | 主题 | +| shape | TDFabShape | TDFabShape.circle | 形状 | +| size | TDFabSize | TDFabSize.large | 大小 | +| text | String? | - | 文本 | +| onClick | VoidCallback? | - | 点击事件 | +| icon | Icon? | - | 图标 | diff --git a/tdesign-component/example/assets/api/footer_api.md b/tdesign-component/example/assets/api/footer_api.md new file mode 100644 index 000000000..51054ebaf --- /dev/null +++ b/tdesign-component/example/assets/api/footer_api.md @@ -0,0 +1,13 @@ +## API +### TDFooter +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| type | TDFooterType | type | 样式 | +| key | | - | | +| logo | String? | - | 品牌图片 | +| text | String | '' | 文字 | +| links | List | const [] | 链接 | +| width | double? | - | 自定义图片宽 | +| height | double? | - | 自定义图片高 | diff --git a/tdesign-component/example/assets/api/form_api.md b/tdesign-component/example/assets/api/form_api.md new file mode 100644 index 000000000..c94d462a0 --- /dev/null +++ b/tdesign-component/example/assets/api/form_api.md @@ -0,0 +1,67 @@ +## API +### TDFormItem +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| type | TDFormItemType | - | 表格单元需要使用的组件类型 | +| child | Widget? | - | 表单子组件 | +| formItemNotifier | | - | | +| label | String? | - | 表单项标签左侧展示的内容 | +| labelWidget | Widget? | - | 自定义标签 | +| help | String? | - | TDInput 默认显示文字 | +| name | String? | - | 表单字段名称 | +| labelAlign | TextAlign? | - | TODO: item 标签对齐方式 | +| contentAlign | TextAlign? | - | 表单显示内容对齐方式: | +| labelWidth | double? | - | 标签宽度,如果提供则覆盖Form的labelWidth | +| tipAlign | TextAlign? | - | 组件提示内容对齐方式 | +| requiredMark | bool? | true | 是否显示必填标记(*) | +| formRules | List? | - | 整个表单的校验规则 | +| itemRule | List? | - | 表单项验证规则 | +| showErrorMessage | bool | true | 是否显示错误信息 | +| indicator | bool? | - | TDTextarea 的属性,指示器 | +| additionInfo | String? | - | TDInput的辅助信息 | +| select | String | '' | 选择器 适用于日期选择器等 | +| selectFn | Function? | - | 选择器方法 适用于日期选择器等 | +| hintText | null | '' | 提示内容 | +| radios | | - | | +| key | | - | | + +``` +``` + ### TDFormValidation +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| validate | String? Function(dynamic) | - | 校验方法 | +| errorMessage | String | - | 错误提示信息 | +| type | TDFormItemType | - | 校验对象的类型 | + +``` +``` + ### TDForm +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| items | List | - | 表单内容 items | +| rules | Map | - | 整个表单字段校验规则 | +| onSubmit | Function | - | 表单提交时触发 | +| data | Map | - | 表单数据 | +| colon | bool? | false | 是否在表单标签字段右侧显示冒号 | +| formContentAlign | TextAlign | TextAlign.left | 表单内容对齐方式: 左对齐、右对齐、居中对齐 | +| isHorizontal | bool | true | 表单排列方式是否为 水平方向 | +| disabled | bool | false | 是否禁用整个表单 | +| errorMessage | Object? | - | 表单信息错误信息配置 | +| formLabelAlign | TextAlign? | TextAlign.left | 表单字段标签的对齐方式: | +| labelWidth | double? | 20.0 | 可以整体设置 label 标签宽度 | +| preventSubmitDefault | bool? | true | 是否阻止表单提交默认事件(表单提交默认事件会刷新页面) | +| requiredMark | bool? | true | 是否显示必填符号(*),默认显示 | +| scrollToFirstError | String? | - | 表单校验不通过时,是否自动滚动到第一个校验不通过的字段,平滑滚动或是瞬间直达。 | +| formShowErrorMessage | bool? | true | 校验不通过时,是否显示错误提示信息,统一控制全部表单项 | +| submitWithWarningMessage | bool? | false | 【讨论中】当校验结果只有告警信息时,是否触发 submit 提交事件 | +| onReset | Function? | - | 表单重置时触发 | +| formController | FormController? | - | 表单控制器 | +| btnGroup | List? | - | 表单按钮组 | diff --git a/tdesign-component/example/assets/api/icon_api.md b/tdesign-component/example/assets/api/icon_api.md new file mode 100644 index 000000000..38deaeff2 --- /dev/null +++ b/tdesign-component/example/assets/api/icon_api.md @@ -0,0 +1,8 @@ +## API +### TDIcons + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDIcons._ | 私有构造方法,不支持外部创建,仅提供静态常量给外部使用 | diff --git a/tdesign-component/example/assets/api/image-viewer_api.md b/tdesign-component/example/assets/api/image-viewer_api.md new file mode 100644 index 000000000..1ab6a0366 --- /dev/null +++ b/tdesign-component/example/assets/api/image-viewer_api.md @@ -0,0 +1,41 @@ +## API +### TDImageViewer + +#### 静态方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| showImageViewer | | required BuildContext context, required List images, List? labels, bool? closeBtn, bool? deleteBtn, bool? showIndex, bool? loop, bool? autoplay, int? duration, Color? bgColor, Color? navBarBgColor, Color? iconColor, TextStyle? labelStyle, TextStyle? indexStyle, Color? modalBarrierColor, bool? barrierDismissible, int? defaultIndex, double? width, double? height, OnIndexChange? onIndexChange, OnClose? onClose, OnDelete? onDelete, bool? ignoreDeleteError, OnImageTap? onTap, OnLongPress? onLongPress, LeftItemBuilder? leftItemBuilder, RightItemBuilder? rightItemBuilder, | 显示图片预览 | + +``` +``` + ### TDImageViewerWidget +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| closeBtn | bool? | - | 是否展示关闭按钮 | +| deleteBtn | bool? | - | 是否显示删除操作 | +| images | List | - | 图片数组 | +| labels | List? | - | 图片描述 | +| showIndex | bool? | - | 是否显示页码 | +| loop | bool? | - | 图片是否循环 | +| autoplay | bool? | - | 图片轮播是否自动播放 | +| duration | int? | - | 自动播放间隔 | +| bgColor | Color? | - | 背景色 | +| navBarBgColor | Color? | - | 导航栏背景色 | +| iconColor | Color? | - | 图标颜色 | +| labelStyle | TextStyle? | - | label文字样式 | +| indexStyle | TextStyle? | - | 页码样式 | +| defaultIndex | int? | - | 默认预览图片所在的下标 | +| onIndexChange | OnIndexChange? | - | 预览图片切换回调 | +| width | double? | - | 图片宽度 | +| height | double? | - | 图片高度 | +| onClose | OnClose? | - | 关闭点击 | +| onDelete | OnDelete? | - | 删除点击 | +| ignoreDeleteError | bool? | false | 是否忽略单张图片删除错误提示 | +| onTap | OnImageTap? | - | 点击图片 | +| onLongPress | OnLongPress? | - | 长按图片 | +| leftItemBuilder | LeftItemBuilder? | - | 左侧自定义操作 | +| rightItemBuilder | RightItemBuilder? | - | 右侧自定义操作 | diff --git a/tdesign-component/example/assets/api/image_api.md b/tdesign-component/example/assets/api/image_api.md new file mode 100644 index 000000000..404490be2 --- /dev/null +++ b/tdesign-component/example/assets/api/image_api.md @@ -0,0 +1,33 @@ +## API +### TDImage +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| imgUrl | String? | - | 图片地址 | +| key | | - | | +| type | TDImageType | TDImageType.roundedSquare | 图片类型 | +| errorWidget | Widget? | - | 失败自定义提示 | +| loadingWidget | Widget? | - | 加载自定义提示 | +| width | double? | - | 自定义宽 | +| height | double? | - | 自定义高 | +| fit | BoxFit? | - | 适配样式 | +| frameBuilder | ImageFrameBuilder? | - | 以下系统Image属性,释义请参考系统[Image]中注释 | +| loadingBuilder | | - | | +| errorBuilder | | - | | +| semanticLabel | | - | | +| excludeFromSemantics | | false | | +| color | | - | | +| opacity | | - | | +| colorBlendMode | | - | | +| alignment | | Alignment.center | | +| repeat | | ImageRepeat.noRepeat | | +| centerSlice | | - | | +| matchTextDirection | | false | | +| gaplessPlayback | | false | | +| isAntiAlias | | false | | +| filterQuality | | FilterQuality.low | | +| cacheHeight | | - | | +| cacheWidth | | - | | +| assetUrl | String? | - | 本地素材地址 | +| imageFile | File? | - | 图片文件路径 | diff --git a/tdesign-component/example/assets/api/indexes_api.md b/tdesign-component/example/assets/api/indexes_api.md new file mode 100644 index 000000000..62b6928f1 --- /dev/null +++ b/tdesign-component/example/assets/api/indexes_api.md @@ -0,0 +1,53 @@ +## API +### TDIndexesAnchor +#### 简介 +索引锚点 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| sticky | bool | - | 索引是否吸顶 | +| text | String | - | 锚点文本 | +| capsuleTheme | bool | - | 是否为胶囊式样式 | +| builderAnchor | Widget? Function(BuildContext context, String index, bool isPinnedToTop)? | - | 索引锚点构建 | +| activeIndex | ValueNotifier | - | 选中索引 | + +``` +``` + ### TDIndexesList +#### 简介 +索引 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| indexList | List | - | 索引字符列表。不传默认 A-Z | +| indexListMaxHeight | double | 0.8 | 索引列表最大高度(父容器高度的百分比,默认0.8) | +| activeIndex | ValueNotifier | - | 选中索引 | +| onSelect | void Function(String newIndex, String oldIndex) | - | 点击侧边栏时触发事件 | +| builderIndex | Widget Function(BuildContext context, String index, bool isActive)? | - | 索引文本自定义构建,包括索引激活左侧提示 | + +``` +``` + ### TDIndexes +#### 简介 +索引 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| indexList | List? | - | 索引字符列表。不传默认 A-Z | +| indexListMaxHeight | double? | 0.8 | 索引列表最大高度(父容器高度的百分比,默认0.8) | +| sticky | bool? | true | 锚点是否吸顶 | +| stickyOffset | double? | 0 | 锚点吸顶时与顶部的距离 | +| capsuleTheme | bool? | false | 锚点是否为胶囊式样式 | +| reverse | bool? | false | 反方向滚动置顶 | +| scrollController | ScrollController? | - | 滚动控制器 | +| onChange | void Function(String index)? | - | 索引发生变更时触发事件 | +| onSelect | void Function(String index)? | - | 点击侧边栏时触发事件 | +| builderContent | Widget? Function(BuildContext context, String index) | - | 内容自定义构建 | +| builderAnchor | Widget? Function(BuildContext context, String index, bool isPinnedToTop)? | - | 锚点自定义构建 | +| builderIndex | Widget Function(BuildContext context, String index, bool isActive)? | - | 索引文本自定义构建,包括索引激活左侧提示 | diff --git a/tdesign-component/example/assets/api/input_api.md b/tdesign-component/example/assets/api/input_api.md new file mode 100644 index 000000000..b7f588640 --- /dev/null +++ b/tdesign-component/example/assets/api/input_api.md @@ -0,0 +1,57 @@ +## API +### TDInput +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| width | double? | - | 输入框宽度(TDCardStyle时必须设置该参数) | +| textStyle | TextStyle? | - | 文本颜色 | +| backgroundColor | Color? | - | 输入框背景色 | +| decoration | Decoration? | - | 输入框样式 | +| leftIcon | Widget? | - | 带图标的输入框 | +| leftLabel | String? | - | 输入框左侧文案 | +| leftLabelStyle | TextStyle? | - | 左侧标签样式 设置该值是若出现像素溢出,请设置letterSpacing: 0 | +| leftLabelSpace | double? | - | 输入框左侧文案间距 | +| required | bool? | - | 是否必填标志(红色*) | +| readOnly | bool | false | 是否只读 | +| autofocus | bool | false | 是否自动获取焦点 | +| obscureText | bool | false | 是否隐藏输入的文字,一般用在密码输入框中 | +| onEditingComplete | VoidCallback? | - | 点击键盘完成按钮时触发的回调 | +| onSubmitted | ValueChanged? | - | 点击键盘完成按钮时触发的回调, 参数值为输入的内容 | +| hintText | String? | - | 提示文案 | +| inputType | TextInputType? | - | 键盘类型,数字、字母 | +| onChanged | ValueChanged? | - | 输入文本变化时回调 | +| inputFormatters | List? | - | 显示输入内容,如限制长度(LengthLimitingTextInputFormatter(6)) | +| inputDecoration | InputDecoration? | - | 自定义输入框样式,默认圆角 | +| maxLines | int | 1 | 最大输入行数 | +| focusNode | FocusNode? | - | 获取或者取消焦点使用 | +| controller | TextEditingController? | - | controller 用户获取或者赋值输入内容 | +| cursorColor | Color? | - | 游标颜色 | +| rightBtn | Widget? | - | 右侧按钮 | +| hintTextStyle | TextStyle? | - | 提示文本颜色,默认为文本颜色 | +| onBtnTap | GestureTapCallback? | - | 右侧按钮点击 | +| labelWidget | Widget? | - | leftLabel右侧组件,支持自定义 | +| leftInfoWidth | double? | - | 输入框左侧的宽度(输入框有16dp的左侧padding,因而左侧部分不用考虑这16dp) | +| leftContentSpace | double? | - | 输入框内容左侧间距 | +| textInputBackgroundColor | Color? | - | 文本框背景色 | +| contentPadding | EdgeInsetsGeometry? | - | textInput内边距 | +| type | TDInputType | TDInputType.normal | 输入框类型 | +| size | TDInputSize | TDInputSize.large | 输入框规格 | +| maxLength | int? | 500 | 最大字数限制 | +| additionInfo | String? | '' | 错误提示信息 | +| additionInfoColor | Color? | - | 错误提示颜色 | +| textAlign | TextAlign? | - | 文字对齐方向 | +| clearIconSize | double? | - | 清除按钮图标大小 | +| onClearTap | GestureTapCallback? | - | 右侧删除点击 | +| needClear | bool | true | 是否需要右侧按钮变为删除 | +| clearBtnColor | Color? | - | 右侧删除按钮颜色 | +| contentAlignment | TextAlign | TextAlign.start | 内容对齐方向 | +| rightWidget | Widget? | - | 右侧自定义组件 特殊类型时生效 | +| showBottomDivider | bool | true | 是否展示底部分割线 | +| cardStyle | TDCardStyle? | - | 卡片默认样式 | +| cardStyleTopText | String? | - | 卡片模式上方文字 | +| inputAction | TextInputAction? | - | 键盘动作类型 | +| spacer | TDInputSpacer | - | 组件各模块间间距 | +| cardStyleBottomText | String? | - | 卡片模式下方文字 | +| onTapOutside | TapRegionCallback? | - | 点击输入框外部区域回调 | diff --git a/tdesign-component/example/assets/api/link_api.md b/tdesign-component/example/assets/api/link_api.md new file mode 100644 index 000000000..8c0528100 --- /dev/null +++ b/tdesign-component/example/assets/api/link_api.md @@ -0,0 +1,21 @@ +## API +### TDLink +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| label | String | - | link 展示的文本 | +| uri | Uri? | - | link 跳转的uri | +| prefixIcon | Icon? | - | 前置 icon | +| suffixIcon | Icon? | - | 后置 icon | +| linkClick | LinkClick? | - | link 被点击之后所采取的动作,会将uri当做参数传入到该方法当中 | +| type | TDLinkType | TDLinkType.basic | link 类型 | +| style | TDLinkStyle | TDLinkStyle.defaultStyle | link 风格 | +| state | TDLinkState | TDLinkState.normal | link 状态 | +| size | TDLinkSize | TDLinkSize.medium | link 大小 | +| color | Color? | - | link 文本的颜色,如果不设置则根据状态和风格进行计算 | +| iconSize | double? | - | link icon 大小,如果不设置则根据状态和风格进行计算 | +| fontSize | double? | - | link 文本的字体大小,如果不设置则根据状态和风格进行计算 | +| leftGapWithIcon | double? | - | 前置icon和文本之间的间隔,如果不设置则根据状态和风格进行计算 | +| rightGapWithIcon | double? | - | 后置icon和文本之间的间隔,如果不设置则根据状态和风格进行计算 | diff --git a/tdesign-component/example/assets/api/loading_api.md b/tdesign-component/example/assets/api/loading_api.md new file mode 100644 index 000000000..162c46a31 --- /dev/null +++ b/tdesign-component/example/assets/api/loading_api.md @@ -0,0 +1,16 @@ +## API +### TDLoading +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| size | TDLoadingSize | - | 尺寸 | +| icon | TDLoadingIcon? | TDLoadingIcon.circle | 图标,支持圆形、点状、菊花状 | +| iconColor | Color? | - | 图标颜色 | +| axis | Axis | Axis.vertical | 文案和图标相对方向 | +| text | String? | - | 文案 | +| refreshWidget | Widget? | - | 失败刷新组件 | +| customIcon | Widget? | - | 自定义图标,优先级高于icon | +| textColor | Color | Colors.black | 文案颜色 | +| duration | int | 2000 | 一次刷新的时间,控制动画速度 | diff --git a/tdesign-component/example/assets/api/message_api.md b/tdesign-component/example/assets/api/message_api.md new file mode 100644 index 000000000..abff18e13 --- /dev/null +++ b/tdesign-component/example/assets/api/message_api.md @@ -0,0 +1,48 @@ +## API +### MessageLink +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| name | String | - | 名称 | +| uri | Uri? | - | 资源链接 | +| color | Color? | - | 颜色 | + +``` +``` + ### MessageMarquee +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| speed | int? | - | 速度 | +| loop | int? | - | 循环次数 | +| delay | int? | - | 延迟时间(毫秒) | + +``` +``` + ### TDMessage +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| closeBtn | dynamic | - | 关闭按钮 | +| content | String? | - | 通知内容 | +| duration | int? | 3000 | 消息内置计时器 | +| icon | dynamic | true | 自定义消息前面的图标 | +| link | dynamic | - | 链接名称 | +| marquee | MessageMarquee? | - | 跑马灯效果 | +| offset | List? | - | 相对于 placement 的偏移量 | +| theme | MessageTheme? | MessageTheme.info | 消息组件风格 info/success/warning/error | +| visible | bool? | true | 是否显示 | +| onCloseBtnClick | VoidCallback? | - | 点击关闭按钮触发 | +| onDurationEnd | VoidCallback? | - | 计时结束后触发 | +| onLinkClick | VoidCallback? | - | 点击链接文本时触发 | + + +#### 静态方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| showMessage | | required BuildContext context, String? content, bool? visible, int? duration, dynamic closeBtn, dynamic icon, dynamic link, MessageMarquee? marquee, List? offset, MessageTheme? theme, VoidCallback? onCloseBtnClick, VoidCallback? onDurationEnd, VoidCallback? onLinkClick, | | diff --git a/tdesign-component/example/assets/api/navbar_api.md b/tdesign-component/example/assets/api/navbar_api.md new file mode 100644 index 000000000..65f0dc748 --- /dev/null +++ b/tdesign-component/example/assets/api/navbar_api.md @@ -0,0 +1,43 @@ +## API +### TDNavBar +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| leftBarItems | List? | - | 左边操作项 | +| rightBarItems | List? | - | 右边操作项 | +| titleWidget | Widget? | - | 标题控件,优先级高于title文案 | +| title | String? | - | 标题文案 | +| titleColor | Color? | - | 标题颜色 | +| titleFont | Font? | - | 标题字体尺寸 | +| titleFontFamily | FontFamily? | - | 标题字体样式 | +| titleFontWeight | FontWeight? | FontWeight.w500 | 标题字体粗细 | +| centerTitle | bool | true | 标题是否居中 | +| opacity | double | 1.0 | 透明度 | +| backgroundColor | Color? | - | 背景颜色 | +| titleMargin | double | 16 | 中间文案左右两边间距 | +| padding | EdgeInsetsGeometry? | - | 内部填充 | +| height | double | 44 | 高度 | +| screenAdaptation | bool | true | 是否进行屏幕适配,默认true | +| useDefaultBack | bool | true | 是否使用默认的返回 | +| onBack | VoidCallback? | - | 返回事件 | +| useBorderStyle | bool | false | 是否使用边框模式 | +| border | TDNavBarItemBorder? | - | 边框 | +| belowTitleWidget | Widget? | - | belowTitleWidget navbar 下方的widget | +| boxShadow | List? | - | 底部阴影 | +| flexibleSpace | Widget? | - | 固定背景 | + +``` +``` + ### TDNavBarItem +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| icon | IconData? | - | 图标 | +| iconColor | Color? | - | 图标颜色 | +| action | TDBarItemAction? | - | 操作回调 | +| iconSize | double? | 24.0 | 图标尺寸 | +| padding | EdgeInsetsGeometry? | - | | +| iconWidget | Widget? | - | 图标组件,优先级高与icon | diff --git a/tdesign-component/example/assets/api/notice-bar_api.md b/tdesign-component/example/assets/api/notice-bar_api.md new file mode 100644 index 000000000..ddbc959dc --- /dev/null +++ b/tdesign-component/example/assets/api/notice-bar_api.md @@ -0,0 +1,46 @@ +## API +### TDNoticeBar +#### 简介 + +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| context | dynamic | - | 文本内容 | +| style | TDNoticeBarStyle? | - | 公告栏样式 | +| left | Widget? | - | 左侧内容(自定义左侧内容,优先级高于prefixIcon) | +| right | Widget? | - | 右侧内容(自定义右侧内容,优先级高于suffixIcon) | +| speed | double? | 50 | 滚动速度 | +| interval | int? | 3000 | 步进滚动间隔时间(毫秒) | +| marquee | bool? | false | 跑马灯效果 | +| direction | Axis? | Axis.horizontal | 滚动方向 | +| theme | TDNoticeBarTheme? | TDNoticeBarTheme.info | 主题 | +| prefixIcon | IconData? | - | 左侧图标 | +| suffixIcon | IconData? | - | 右侧图标 | +| onTap | ValueChanged? | - | 点击事件 | +| height | double | 22 | 文字高度 (当使用prefixIcon或suffixIcon时,icon大小值等于该属性) | +| maxLines | int? | 1 | 文本行数(仅静态有效) | + +``` +``` + ### TDNoticeBarStyle +#### 简介 +公告栏样式 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext? | - | 上下文 | +| backgroundColor | Color? | - | 公告栏背景色 | +| textStyle | TextStyle? | - | 公告栏内容样式 | +| leftIconColor | Color? | - | 公告栏左侧图标颜色 | +| rightIconColor | Color? | - | 公告栏右侧图标颜色 | +| padding | EdgeInsetsGeometry? | - | 公告栏内边距 | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDNoticeBarStyle.generateTheme | 根据主题生成样式 | diff --git a/tdesign-component/example/assets/api/picker_api.md b/tdesign-component/example/assets/api/picker_api.md new file mode 100644 index 000000000..84a2a0e5b --- /dev/null +++ b/tdesign-component/example/assets/api/picker_api.md @@ -0,0 +1,88 @@ +## API +### TDMultiPicker +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| title | String? | - | 选择器标题 | +| onConfirm | MultiPickerCallback? | - | 选择器确认按钮回调 | +| onCancel | MultiPickerCallback? | - | 选择器取消按钮回调 | +| data | Map | - | 总的数据 | +| pickerHeight | double | - | | +| pickerItemCount | int | - | 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 | +| initialIndexes | List? | - | 若为null表示全部从零开始 | +| rightText | String? | - | 右侧按钮文案 | +| leftText | String? | - | 左侧按钮文案 | +| leftTextStyle | TextStyle? | - | 自定义左侧文案样式 | +| rightTextStyle | TextStyle? | - | 自定义右侧文案样式 | +| centerTextStyle | TextStyle? | - | 自定义中间文案样式 | +| titleHeight | double? | - | 标题高度 | +| topPadding | double? | - | 顶部填充 | +| leftPadding | double? | - | 左边填充 | +| rightPadding | double? | - | 右边填充 | +| titleDividerColor | Color? | - | 标题分割线颜色 | +| backgroundColor | Color? | - | 背景颜色 | +| topRadius | double? | - | 顶部圆角 | +| padding | EdgeInsets? | - | 适配padding | +| itemDistanceCalculator | ItemDistanceCalculator? | - | 不同距离自选项计算策略 | +| customSelectWidget | Widget? | - | 自定义选择框样式 | +| itemBuilder | ItemBuilderType? | - | 自定义item构建 | +| key | | - | | + +``` +``` + ### TDMultiLinkedPicker +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| title | String? | - | 选择器标题 | +| onConfirm | MultiPickerCallback? | - | 选择器确认按钮回调 | +| onCancel | MultiPickerCallback? | - | 选择器取消按钮回调 | +| selectedData | List | - | 选中数据 | +| data | Map | - | 总的数据 | +| columnNum | int | - | 总列数 | +| pickerHeight | double | 200 | | +| pickerItemCount | int | 5 | 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 | +| customSelectWidget | Widget? | - | 自定义选择框样式 | +| rightText | String? | - | 右侧按钮文案 | +| leftText | String? | - | 左侧按钮文案 | +| leftTextStyle | TextStyle? | - | 自定义左侧文案样式 | +| rightTextStyle | TextStyle? | - | 自定义右侧文案样式 | +| centerTextStyle | TextStyle? | - | 自定义中间文案样式 | +| titleHeight | double? | - | 标题高度 | +| topPadding | double? | - | 顶部填充 | +| leftPadding | double? | - | 左边填充 | +| rightPadding | double? | - | 右边填充 | +| titleDividerColor | Color? | - | 标题分割线颜色 | +| backgroundColor | Color? | - | 背景颜色 | +| topRadius | double? | - | 顶部圆角 | +| padding | EdgeInsets? | - | 适配padding | +| itemDistanceCalculator | ItemDistanceCalculator? | - | 不同距离自选项计算策略 | +| itemBuilder | ItemBuilderType? | - | 自定义item构建 | +| keepSameSelection | bool | false | 是否保留相同选项 | +| key | | - | | + +``` +``` + ### MultiLinkedPickerModel +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| data | Map | - | 总的数据 | +| columnNum | int | - | 总列数 | +| initialData | | - | | +| keepSameSelection | bool | false | 是否保留相同选项 | + +``` +``` + ### TDPicker + +#### 静态方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| showDatePicker | | required null context, required String title, required DatePickerCallback? onConfirm, DatePickerCallback? onCancel, bool useYear, bool useMonth, bool useDay, bool useHour, bool useMinute, bool useSecond, bool useWeekDay, Color? barrierColor, List dateStart, List? dateEnd, List? initialDate, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, Color? titleDividerColor, Widget? customSelectWidget, Duration duration, double pickerHeight, bool isTimeUnit, Function(int wheelIndex, int index)? onSelectedItemChanged, int pickerItemCount, List Function(DateTypeKey key, List nums)? filterItems, ItemBuilderType? itemBuilder, | 显示时间选择器 | +| showMultiPicker | | required null context, String? title, required MultiPickerCallback? onConfirm, MultiPickerCallback? onCancel, required List> data, List? initialIndexes, Duration duration, Color? barrierColor, double pickerHeight, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, Color? titleDividerColor, double? topPadding, int pickerItemCount, Widget? customSelectWidget, ItemBuilderType? itemBuilder, | 显示多级选择器 | +| showMultiLinkedPicker | | required null context, String? title, required MultiPickerCallback? onConfirm, MultiPickerCallback? onCancel, required Map data, required int columnNum, required List initialData, Duration duration, Color? barrierColor, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, double pickerHeight, Color? titleDividerColor, Widget? customSelectWidget, double? topPadding, bool keepSameSelection, int pickerItemCount, | 显示多级联动选择器 | diff --git a/tdesign-component/example/assets/api/popover_api.md b/tdesign-component/example/assets/api/popover_api.md new file mode 100644 index 000000000..dfbd2d278 --- /dev/null +++ b/tdesign-component/example/assets/api/popover_api.md @@ -0,0 +1,34 @@ +## API +### TDPopoverWidget +#### 简介 + +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| context | BuildContext | - | 上下文 | +| content | String? | - | 显示内容 | +| contentWidget | Widget? | - | 自定义内容 | +| offset | double | 4 | 偏移 | +| theme | TDPopoverTheme? | - | 弹出气泡主题 | +| placement | TDPopoverPlacement? | - | 浮层出现位置 | +| showArrow | bool? | true | 是否显示浮层箭头 | +| arrowSize | double | 8 | 箭头大小 | +| padding | EdgeInsetsGeometry? | - | 内容内边距 | +| width | double? | - | 内容宽度(包含padding,实际高度:height - paddingLeft - paddingRight) | +| height | double? | - | 内容高度(包含padding,实际高度:height - paddingTop - paddingBottom) | +| onTap | OnTap? | - | 点击事件 | +| onLongTap | OnLongTap? | - | 长按事件 | + +``` +``` + ### TDPopover +#### 简介 + + +#### 静态方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| showPopover | | required BuildContext context, String? content, Widget? contentWidget, double offset, TDPopoverTheme? theme, bool closeOnClickOutside, TDPopoverPlacement? placement, bool? showArrow, double arrowSize, EdgeInsetsGeometry? padding, double? width, double? height, Color? overlayColor, OnTap? onTap, OnLongTap? onLongTap, | | diff --git a/tdesign-component/example/assets/api/popup_api.md b/tdesign-component/example/assets/api/popup_api.md new file mode 100644 index 000000000..dd1afeae4 --- /dev/null +++ b/tdesign-component/example/assets/api/popup_api.md @@ -0,0 +1,94 @@ +## API +### TDSlidePopupRoute +#### 简介 +从屏幕的某个方向滑动弹出的Dialog框的路由,比如从顶部、底部、左、右滑出页面 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| builder | WidgetBuilder | - | 控件构建器 | +| barrierLabel | | - | | +| modalBarrierColor | Color? | Colors.black54 | 蒙层颜色 | +| isDismissible | bool | true | 点击蒙层能否关闭 | +| modalBarrierFull | bool | false | 是否全屏显示蒙层 | +| slideTransitionFrom | SlideTransitionFrom | SlideTransitionFrom.bottom | 设置从屏幕的哪个方向滑出 | +| modalWidth | double? | - | 弹出框宽度 | +| modalHeight | double? | - | 弹出框高度 | +| modalTop | double? | 0 | 弹出框顶部距离 | +| modalLeft | double? | 0 | 弹出框左侧距离 | +| open | VoidCallback? | - | 打开前事件 | +| opened | VoidCallback? | - | 打开后事件 | +| close | VoidCallback? | - | 关闭前事件 | +| barrierClick | VoidCallback? | - | 蒙层点击事件,仅在[modalBarrierFull]为false时触发 | +| focusMove | bool | false | 是否有输入框获取焦点时整体平移避免输入框被遮挡 | + +``` +``` + ### TDPopupBottomDisplayPanel +#### 简介 +右上角带关闭的底部浮层面板 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| child | | - | | +| title | | - | | +| titleColor | | - | | +| titleFontSize | double? | - | 标题字体大小 | +| titleLeft | bool | false | 标题是否靠左 | +| hideClose | bool | false | 是否隐藏关闭按钮 | +| closeColor | Color? | - | 关闭按钮颜色 | +| closeSize | double? | - | 关闭按钮图标尺寸 | +| closeClick | PopupClick? | - | 关闭按钮点击回调 | +| backgroundColor | | - | | +| radius | | - | | +| draggable | | - | | +| maxHeightRatio | | - | | +| minHeightRatio | | - | | + +``` +``` + ### TDPopupBottomConfirmPanel +#### 简介 +带确认的底部浮层面板 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| child | | - | | +| title | | - | | +| titleColor | | - | | +| leftText | String? | - | 左边文本 | +| leftTextColor | Color? | - | 左边文本颜色 | +| leftClick | PopupClick? | - | 左边文本点击回调 | +| rightText | String? | - | 右边文本 | +| rightTextColor | Color? | - | 右边文本颜色 | +| rightClick | PopupClick? | - | 右边文本点击回调 | +| titleFontSize | double? | - | 标题字体大小 | +| leftTextFontSize | double? | - | 左边文本字体大小 | +| rightTextFontSize | double? | - | 右边文本字体大小 | +| backgroundColor | | - | | +| radius | | - | | +| draggable | | - | | +| maxHeightRatio | | - | | +| minHeightRatio | | - | | + +``` +``` + ### TDPopupCenterPanel +#### 简介 +居中浮层面板 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| child | Widget | - | 子控件 | +| closeUnderBottom | bool | false | 关闭按钮是否在视图框下方 | +| closeColor | Color? | - | 关闭按钮颜色 | +| closeClick | PopupClick? | - | 关闭按钮点击回调 | +| backgroundColor | Color? | - | 背景颜色 | +| radius | double? | - | 圆角 | +| closeSize | double? | - | 关闭按钮图标尺寸 | diff --git a/tdesign-component/example/assets/api/progress_api.md b/tdesign-component/example/assets/api/progress_api.md new file mode 100644 index 000000000..9412fd6be --- /dev/null +++ b/tdesign-component/example/assets/api/progress_api.md @@ -0,0 +1,24 @@ +## API +### TDProgress +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| type | TDProgressType | - | 进度条类型 | +| value | double? | - | 进度值 (0.0 到 1.0 之间的正数) | +| label | TDLabelWidget? | - | 进度条标签 | +| progressStatus | TDProgressStatus | TDProgressStatus.primary | 进度条状态 | +| progressLabelPosition | TDProgressLabelPosition | TDProgressLabelPosition.inside | 标签显示位置 | +| strokeWidth | double? | - | 进度条粗细 (正数) | +| color | Color? | - | 进度条颜色 | +| backgroundColor | Color? | - | 进度条背景颜色 | +| linearBorderRadius | BorderRadiusGeometry? | - | 条形进度条末端形状 | +| circleRadius | double? | - | 环形进度条半径 (正数) | +| showLabel | bool | true | 是否显示标签 | +| customProgressLabel | Widget? | - | 自定义标签 | +| labelWidgetWidth | double? | - | 自定义标签宽度 | +| labelWidgetAlignment | Alignment? | - | 自定义标签对齐方式 | +| onTap | VoidCallback? | - | 点击事件 | +| onLongPress | VoidCallback? | - | 长按事件 | +| animationDuration | int? | 300 | 动画持续时间 (正整数,单位为毫秒) | diff --git a/tdesign-component/example/assets/api/pull-down-refresh_api.md b/tdesign-component/example/assets/api/pull-down-refresh_api.md new file mode 100644 index 000000000..6a3d92683 --- /dev/null +++ b/tdesign-component/example/assets/api/pull-down-refresh_api.md @@ -0,0 +1,45 @@ +## API +### TDRefreshHeader +#### 简介 +TDesign刷新头部 + 结合EasyRefresh类实现下拉刷新,继承自Header类,字段含义与父类一致 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | Key? | - | Key | +| extent | double? | 48.0 | Header容器高度 | +| triggerOffset | | - | | +| triggerDistance | double | 48.0 | 触发刷新任务的偏移量,同[triggerOffset] | +| clamping | | - | | +| float | bool | false | 是否悬浮 | +| processedDuration | | - | | +| completeDuration | Duration? | - | 完成延时 | +| hapticFeedback | | - | | +| enableHapticFeedback | bool | true | 开启震动反馈 | +| infiniteOffset | double? | - | 无限刷新偏移量 | +| enableInfiniteRefresh | bool | false | 是否开启无限刷新 | +| infiniteHitOver | | - | | +| overScroll | bool | true | 越界滚动([enableInfiniteRefresh]为true或[infiniteOffset]有值时生效) | +| loadingIcon | TDLoadingIcon | TDLoadingIcon.circle | loading样式 | +| backgroundColor | Color? | - | 背景颜色 | +| spring | | - | | +| horizontalSpring | | - | | +| readySpringBuilder | | - | | +| horizontalReadySpringBuilder | | - | | +| springRebound | | - | | +| frictionFactor | | - | | +| horizontalFrictionFactor | | - | | +| safeArea | | false | | +| hitOver | | - | | +| position | | - | | +| secondaryTriggerOffset | | - | | +| secondaryVelocity | | - | | +| secondaryDimension | | - | | +| secondaryCloseTriggerOffset | | - | | +| notifyWhenInvisible | | - | | +| listenable | | - | | +| triggerWhenReach | | - | | +| triggerWhenRelease | | - | | +| triggerWhenReleaseNoWait | | - | | +| maxOverOffset | | - | | diff --git a/tdesign-component/example/assets/api/radio_api.md b/tdesign-component/example/assets/api/radio_api.md new file mode 100644 index 000000000..efda7ff1c --- /dev/null +++ b/tdesign-component/example/assets/api/radio_api.md @@ -0,0 +1,66 @@ +## API +### TDRadio +#### 简介 +单选框按钮,继承自TDCheckbox,字段含义与父类一致 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| id | | - | | +| key | | - | | +| title | | - | | +| titleFont | | - | | +| subTitle | | - | | +| subTitleFont | | - | | +| enable | | true | | +| subTitleMaxLine | | 1 | | +| titleMaxLine | | 1 | | +| selectColor | | - | | +| disableColor | | - | | +| customContentBuilder | | - | | +| spacing | | - | | +| cardMode | | - | | +| showDivider | bool | - | 是否显示下划线 | +| size | | TDCheckBoxSize.small | | +| radioStyle | TDRadioStyle | TDRadioStyle.circle | 单选框按钮样式 | +| contentDirection | | TDContentDirection.right | | +| customIconBuilder | | - | | +| titleColor | | - | | +| subTitleColor | | - | | +| backgroundColor | | - | | +| checkBoxLeftSpace | | - | | +| insetSpacing | | - | | +| customSpace | | - | | + +``` +``` + ### TDRadioGroup +#### 简介 +RadioGroup分组对象,继承自TDCheckboxGroup,字段含义与父类一致 + RadioGroup应该嵌套在RadioGroup内,所有在RadioGroup的RadioButton只能有一个被选中 + + cardMode: 使用卡片样式,需要配合direction 和 directionalTdRadios 使用, + 组合为横向、纵向卡片,同时需要在每个TDRadio上设置cardMode参数。 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| child | | - | | +| direction | | - | | +| directionalTdRadios | | - | | +| selectId | | - | | +| passThrough | | - | | +| cardMode | | false | | +| strictMode | bool | true | 严格模式下,用户不能取消勾选,只能切换选择项, | +| radioCheckStyle | TDRadioStyle? | - | 勾选样式 | +| titleMaxLine | | - | | +| customIconBuilder | | - | | +| customContentBuilder | | - | | +| spacing | | - | | +| rowCount | int | 1 | 每行几列 | +| contentDirection | | - | | +| onRadioGroupChange | | - | | +| showDivider | bool | false | 是否显示下划线 | +| divider | Widget? | - | 自定义下划线 | +| controller | | - | | diff --git a/tdesign-component/example/assets/api/rate_api.md b/tdesign-component/example/assets/api/rate_api.md new file mode 100644 index 000000000..502a4b326 --- /dev/null +++ b/tdesign-component/example/assets/api/rate_api.md @@ -0,0 +1,26 @@ +## API +### TDRate +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| allowHalf | bool? | false | 是否允许半选 | +| color | List? | - | 评分图标的颜色,示例:[选中颜色] / [选中颜色,未选中颜色],默认:[TDTheme.of(context).warningColor5, TDTheme.of(context).grayColor4] | +| count | int? | 5 | 评分的数量 | +| disabled | bool? | false | 是否禁用评分 | +| gap | double? | - | 评分图标的间距,默认:TDTheme.of(context).spacer8 | +| icon | List? | - | 自定义评分图标,[选中和未选中图标] / [选中图标,未选中图标],默认:[TDIcons.star_filled] | +| placement | PlacementEnum? | PlacementEnum.top | 选择评分弹框的位置,值为[PlacementEnum.none]表示不显示评分弹框。 | +| showText | bool? | false | 是否显示对应的辅助文字 | +| size | double? | 24.0 | 评分图标的大小 | +| texts | List? | const ['极差', '失望', '一般', '满意', '惊喜'] | 评分等级对应的辅助文字, | +| textWidth | double? | 48.0 | 评分等级对应的辅助文字宽度 | +| builderText | Widget Function(BuildContext context, double value)? | - | 评分等级对应的辅助文字自定义构建,优先级高于[texts] | +| value | double? | 0 | 选择评分的值 | +| onChange | void Function(double value)? | - | 评分数改变时触发 | +| direction | Axis? | Axis.horizontal | 评分图标与辅助文字的布局方向 | +| mainAxisAlignment | MainAxisAlignment? | MainAxisAlignment.start | 评分图标与辅助文字的主轴对齐方式 | +| crossAxisAlignment | CrossAxisAlignment? | CrossAxisAlignment.center | 评分图标与辅助文字的交叉轴对齐方式 | +| mainAxisSize | MainAxisSize? | MainAxisSize.min | 评分图标与辅助文字主轴方向上如何占用空间 | +| iconTextGap | double? | - | 评分图标与辅助文字的间距,默认:[TDTheme.of(context).spacer16] | diff --git a/tdesign-component/example/assets/api/result_api.md b/tdesign-component/example/assets/api/result_api.md new file mode 100644 index 000000000..13674b653 --- /dev/null +++ b/tdesign-component/example/assets/api/result_api.md @@ -0,0 +1,12 @@ +## API +### TDResult +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| description | String? | - | 描述文本,用于提供额外信息 | +| icon | Widget? | - | 图标组件,用于在结果中显示一个图标 | +| titleStyle | TextStyle? | - | 自定义字体样式,用于设置标题文本的样式 | +| theme | TDResultTheme | TDResultTheme.defaultTheme | 主题样式,定义了结果组件的视觉风格 | +| title | String | '' | 标题文本,显示结果的主要信息 | diff --git a/tdesign-component/example/assets/api/search_api.md b/tdesign-component/example/assets/api/search_api.md new file mode 100644 index 000000000..c1fb9b0b7 --- /dev/null +++ b/tdesign-component/example/assets/api/search_api.md @@ -0,0 +1,30 @@ +## API +### TDSearchBar +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| placeHolder | String? | - | 预设文案 | +| style | TDSearchStyle? | TDSearchStyle.square | 样式 | +| alignment | TDSearchAlignment? | TDSearchAlignment.left | 对齐方式,居中或这头部对齐 | +| onTextChanged | TDSearchBarEvent? | - | 文字改变回调 | +| onSubmitted | TDSearchBarEvent? | - | 提交回调 | +| onEditComplete | TDSearchBarCallBack? | - | 编辑完成回调 | +| onTapOutside | | - | | +| onInputClick | GestureTapCallback? | - | 输入框点击事件 | +| autoHeight | bool | false | 是否自动计算高度 | +| padding | EdgeInsets | const EdgeInsets.fromLTRB(16, 8, 16, 8) | 内部填充 | +| autoFocus | bool | false | 是否自动获取焦点 | +| mediumStyle | bool | false | 是否在导航栏中的样式 | +| cursorHeight | double? | - | 光标的高 | +| needCancel | bool | false | 是否需要取消按钮 | +| controller | TextEditingController? | - | 控制器 | +| backgroundColor | Color? | Colors.white | 背景颜色 | +| action | String | '' | 自定义操作文字 | +| onActionClick | TDSearchBarEvent? | - | 自定义操作回调 | +| onClearClick | TDSearchBarClearEvent? | - | 自定义操作回调 | +| focusNode | FocusNode? | - | 自定义焦点 | +| inputAction | TextInputAction? | - | 键盘动作类型 | +| enabled | bool? | - | 是否禁用 | +| readOnly | bool? | - | 是否只读 | diff --git a/tdesign-component/example/assets/api/side-bar_api.md b/tdesign-component/example/assets/api/side-bar_api.md new file mode 100644 index 000000000..19587a02b --- /dev/null +++ b/tdesign-component/example/assets/api/side-bar_api.md @@ -0,0 +1,37 @@ +## API +### TDSideBar +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| value | int? | - | 选项值 | +| defaultValue | int? | - | 默认值 | +| selectedColor | Color? | - | 选中值后颜色 | +| children | List | const [] | 单项 | +| onChanged | ValueChanged? | - | 选中值发生变化(Controller控制) | +| onSelected | ValueChanged? | - | 选中值发生变化(点击事件) | +| height | double? | - | 高度 | +| controller | TDSideBarController? | - | 控制器 | +| contentPadding | EdgeInsetsGeometry? | - | 自定义文本框内边距 | +| selectedTextStyle | TextStyle? | - | 选中样式 | +| style | TDSideBarStyle | TDSideBarStyle.normal | 样式 | +| loading | bool? | - | 加载效果 | +| loadingWidget | Widget? | - | 自定义加载动画 | +| selectedBgColor | Color? | - | 选择的背景颜色 | +| unSelectedBgColor | Color? | - | 未选择的背景颜色 | + +``` +``` + ### TDSideBarItem +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| badge | TDBadge? | - | 徽标 | +| disabled | bool | false | 是否禁用 | +| icon | IconData? | - | 图标 | +| textStyle | TextStyle? | - | 标签样式 | +| label | String | '' | 标签 | +| value | int | -1 | 值 | diff --git a/tdesign-component/example/assets/api/skeleton_api.md b/tdesign-component/example/assets/api/skeleton_api.md new file mode 100644 index 000000000..aa19855d0 --- /dev/null +++ b/tdesign-component/example/assets/api/skeleton_api.md @@ -0,0 +1,79 @@ +## API +### TDSkeletonRowColStyle +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| rowSpacing | double Function(BuildContext) | _defaultRowSpacing | 行间距 | + +``` +``` + ### TDSkeletonRowCol +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| objects | List> | - | 行列对象 | +| style | TDSkeletonRowColStyle | const TDSkeletonRowColStyle() | 样式 | + +``` +``` + ### TDSkeletonRowColObjStyle +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| background | Color Function(BuildContext) | _defaultBackground | 背景颜色 | +| borderRadius | double Function(BuildContext) | _textBorderRadius | 圆角 | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDSkeletonRowColObjStyle.circle | 圆形 | +| TDSkeletonRowColObjStyle.rect | 矩形 | +| TDSkeletonRowColObjStyle.text | 文本 | +| TDSkeletonRowColObjStyle.spacer | 空白占位符 | + +``` +``` + ### TDSkeletonRowColObj +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| width | double? | - | 宽度 | +| height | double? | 16 | 高度 | +| flex | int? | 1 | 弹性因子 | +| margin | EdgeInsets | EdgeInsets.zero | 间距 | +| style | TDSkeletonRowColObjStyle | const TDSkeletonRowColObjStyle() | 样式 | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDSkeletonRowColObj.circle | 圆形 | +| TDSkeletonRowColObj.rect | 矩形 | +| TDSkeletonRowColObj.text | 文本 | +| TDSkeletonRowColObj.spacer | 空白占位符 | + +``` +``` + ### TDSkeleton +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| animation | TDSkeletonAnimation? | - | 动画效果 | +| delay | int | 0 | 延迟显示加载时间 | +| theme | | TDSkeletonTheme.text | | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDSkeleton.fromRowCol | 从行列框架创建骨架屏 | diff --git a/tdesign-component/example/assets/api/slider_api.md b/tdesign-component/example/assets/api/slider_api.md new file mode 100644 index 000000000..f7db3f8c5 --- /dev/null +++ b/tdesign-component/example/assets/api/slider_api.md @@ -0,0 +1,36 @@ +## API +### TDSlider +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| value | RangeValues | - | 默认值 | +| boxDecoration | Decoration? | - | 自定义盒子样式 | +| onChanged | ValueChanged? | - | 滑动变化监听 | +| sliderThemeData | TDSliderThemeData? | - | 样式 | +| leftLabel | String? | - | 左侧标签 | +| rightLabel | String? | - | 右侧标签 | +| onChangeStart | ValueChanged? | - | 滑动开始监听 | +| onChangeEnd | ValueChanged? | - | 滑动结束监听 | +| onTap | Function(Position position, Offset offset, double value)? | - | | +| onThumbTextTap | Function(Position position, Offset offset, double value)? | - | Thumb 点击浮标文字 位置、坐标、当前值 | + +``` +``` + ### TDRangeSlider +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| value | RangeValues | - | 默认值 | +| boxDecoration | Decoration? | - | 自定义盒子样式 | +| onChanged | ValueChanged? | - | 滑动变化监听 | +| sliderThemeData | TDSliderThemeData? | - | 样式 | +| leftLabel | String? | - | 左侧标签 | +| rightLabel | String? | - | 右侧标签 | +| onChangeStart | ValueChanged? | - | 滑动开始监听 | +| onChangeEnd | ValueChanged? | - | 滑动结束监听 | +| onTap | Function(Position position, Offset offset, double value)? | - | | +| onThumbTextTap | Function(Position position, Offset offset, double value)? | - | Thumb 点击浮标文字 位置、坐标、当前值 | diff --git a/tdesign-component/example/assets/api/stepper_api.md b/tdesign-component/example/assets/api/stepper_api.md new file mode 100644 index 000000000..80bbcea5b --- /dev/null +++ b/tdesign-component/example/assets/api/stepper_api.md @@ -0,0 +1,21 @@ +## API +### TDStepper +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| disableInput | bool | false | 禁用输入框 | +| disabled | bool | false | 禁用全部操作 | +| inputWidth | double? | - | 禁用全部操作 | +| eventController | StreamController? | - | 事件控制器 | +| max | int | 100 | 最大值 | +| min | int | 0 | 最小值 | +| size | TDStepperSize | TDStepperSize.medium | 组件尺寸 | +| step | int | 1 | 步长 | +| theme | TDStepperTheme | TDStepperTheme.normal | 组件风格 | +| value | int? | 0 | 值 | +| defaultValue | int? | 0 | 默认值 | +| onBlur | VoidCallback? | - | 输入框失去焦点时触发 | +| onChange | ValueChanged? | - | 数值发生变更时触发 | +| onOverlimit | TDStepperOverlimitFunction? | - | 数值超出限制时触发 | diff --git a/tdesign-component/example/assets/api/steps_api.md b/tdesign-component/example/assets/api/steps_api.md new file mode 100644 index 000000000..ddb5f71bd --- /dev/null +++ b/tdesign-component/example/assets/api/steps_api.md @@ -0,0 +1,28 @@ +## API +### TDStepsItemData +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| title | String? | - | 标题 | +| content | String? | - | 内容 | +| successIcon | IconData? | - | 成功图标 | +| errorIcon | IconData? | - | 失败图标 | +| customContent | Widget? | - | 自定义内容 | +| customTitle | Widget? | - | 自定义标题 | + +``` +``` + ### TDSteps +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| steps | List | - | 步骤条数据 | +| activeIndex | int | 0 | 步骤条当前激活的索引 | +| direction | TDStepsDirection | TDStepsDirection.horizontal | 步骤条方向 | +| status | TDStepsStatus | TDStepsStatus.success | 步骤条状态 | +| simple | bool | false | 步骤条simple模式 | +| readOnly | bool | false | 步骤条readOnly模式 | +| verticalSelect | bool | false | 步骤条垂直自定义步骤条选择模式 | diff --git a/tdesign-component/example/assets/api/swipe-cell_api.md b/tdesign-component/example/assets/api/swipe-cell_api.md new file mode 100644 index 000000000..c03f655d3 --- /dev/null +++ b/tdesign-component/example/assets/api/swipe-cell_api.md @@ -0,0 +1,31 @@ +## API +### TDSwipeCell +#### 简介 +滑动单元格组件 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| slidableKey | Key? | - | 滑动组件的 Key | +| cell | Widget | - | 单元格 [TDCell] | +| disabled | bool? | false | 是否禁用滑动 | +| opened | List? | const [false, false] | 默认打开,[left, right] | +| right | TDSwipeCellPanel? | - | 右侧滑动操作项面板 | +| left | TDSwipeCellPanel? | - | 左侧滑动操作项面板 | +| onChange | Function(TDSwipeDirection direction, bool open)? | - | 滑动展开事件 | +| controller | SlidableController? | - | 自定义控制滑动窗口 | +| groupTag | Object? | - | 组,配置后,[closeWhenOpened]、[closeWhenTapped]才起作用 | +| closeWhenOpened | bool? | true | 当同一组([groupTag])中的一个[TDSwipeCell]打开时,是否关闭组中的所有其他[TDSwipeCell] | +| closeWhenTapped | bool? | true | 当同一组([groupTag])中的一个[TDSwipeCell]被点击时,是否应该关闭组中的所有[TDSwipeCell] | +| dragStartBehavior | DragStartBehavior? | DragStartBehavior.start | 处理拖动开始行为的方式[GestureDetector.dragStartBehavior] | +| direction | Axis? | Axis.horizontal | 可拖动的方向 | +| duration | Duration? | const Duration(milliseconds: 200) | 打开关闭动画时长 | + + +#### 静态方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| close | | required Object? tag, SlidableController? current, | 根据[groupTag]关闭[TDSwipeCell] current:保留当前不关闭 | +| of | | required BuildContext context, | 获取上下文最近的[controller] | diff --git a/tdesign-component/example/assets/api/swiper_api.md b/tdesign-component/example/assets/api/swiper_api.md new file mode 100644 index 000000000..111eb39f8 --- /dev/null +++ b/tdesign-component/example/assets/api/swiper_api.md @@ -0,0 +1,33 @@ +## API +### TDSwiperPagination +#### 简介 +TDesign风格的Swiper指示器样式,与flutter_swiper的Swiper结合使用 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| alignment | Alignment? | - | 当 scrollDirection== Axis.horizontal 时,默认Alignment.bottomCenter | +| key | Key? | - | | +| margin | EdgeInsetsGeometry | const EdgeInsets.all(10.0) | 指示器和container之间的距离 | +| builder | SwiperPlugin | TDSwiperPagination.dots | 具体样式 | + +``` +``` + ### TDPageTransformer +#### 简介 +TD默认PageTransformer +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| fade | double? | - | 淡化比例 | +| scale | double? | - | 缩放比例 | +| margin | double? | - | 左右间隔 | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDPageTransformer.margin | 普通margin的卡片式 | +| TDPageTransformer.scaleAndFade | 缩放或透明的卡片式 | diff --git a/tdesign-component/example/assets/api/switch_api.md b/tdesign-component/example/assets/api/switch_api.md new file mode 100644 index 000000000..468b74925 --- /dev/null +++ b/tdesign-component/example/assets/api/switch_api.md @@ -0,0 +1,20 @@ +## API +### TDSwitch +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| enable | bool | true | 是否可点击 | +| isOn | bool | false | 是否打开 | +| size | TDSwitchSize? | TDSwitchSize.medium | 尺寸:大、中、小 | +| type | TDSwitchType? | TDSwitchType.fill | 类型:填充、文本、加载 | +| trackOnColor | Color? | - | 开启时轨道颜色 | +| trackOffColor | Color? | - | 关闭时轨道颜色 | +| thumbContentOnColor | Color? | - | 开启时ThumbView的颜色 | +| thumbContentOffColor | Color? | - | 关闭时ThumbView的颜色 | +| thumbContentOnFont | TextStyle? | - | 开启时ThumbView的字体样式 | +| thumbContentOffFont | TextStyle? | - | 关闭时ThumbView的字体样式 | +| onChanged | OnSwitchChanged? | - | 改变事件 | +| openText | String? | - | 打开文案 | +| closeText | String? | - | 关闭文案 | diff --git a/tdesign-component/example/assets/api/tab-bar_api.md b/tdesign-component/example/assets/api/tab-bar_api.md new file mode 100644 index 000000000..3e2ad95b1 --- /dev/null +++ b/tdesign-component/example/assets/api/tab-bar_api.md @@ -0,0 +1,92 @@ +## API +### BadgeConfig +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| showBadge | bool | - | 是否展示消息 | +| tdBadge | TDBadge? | - | 消息样式(未设置但showBadge为true,则默认使用红点) | +| badgeTopOffset | double? | - | 消息顶部偏移量 | +| badgeRightOffset | double? | - | 消息右侧偏移量 | + +``` +``` + ### TDBottomTabBarTabConfig +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| onTap | GestureTapCallback? | - | tab点击事件 | +| selectedIcon | Widget? | - | 选中时图标 | +| unselectedIcon | Widget? | - | 未选中时图标 | +| tabText | String? | - | tab文本 | +| selectTabTextStyle | TextStyle? | - | 文本已选择样式 basicType为text时必填 | +| unselectTabTextStyle | TextStyle? | - | 文本未选择样式 basicType为text时必填 | +| badgeConfig | BadgeConfig? | - | 消息配置 | +| popUpButtonConfig | TDBottomTabBarPopUpBtnConfig? | - | 弹窗配置 | +| onLongPress | GestureLongPressCallback? | - | 长按事件 | +| allowMultipleTaps | bool | false | onTap方法允许点击多次 | + +``` +``` + ### TDBottomTabBar +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| basicType | TDBottomTabBarBasicType | basicType | 基本样式(纯文本、纯图标、图标+文本) | +| key | | - | | +| componentType | TDBottomTabBarComponentType? | TDBottomTabBarComponentType.label | 选项样式 默认label | +| outlineType | TDBottomTabBarOutlineType? | TDBottomTabBarOutlineType.filled | 标签栏样式 默认filled | +| navigationTabs | List | - | tabs配置 | +| barHeight | double? | _kDefaultTabBarHeight | tab高度 | +| useVerticalDivider | bool? | - | 是否使用竖线分隔(如果选项样式为label则强制为false) | +| dividerHeight | double? | - | 分割线高度(可选) | +| dividerThickness | double? | - | 分割线厚度(可选) | +| dividerColor | Color? | - | 分割线颜色(可选) | +| showTopBorder | bool? | true | 是否展示bar上边线(设置为true 但是topBorder样式未设置,则使用默认值,非胶囊型才生效) | +| topBorder | BorderSide? | - | 上边线样式 | +| useSafeArea | bool | true | 使用安全区域 | +| selectedBgColor | Color? | - | 选中时背景颜色 | +| unselectedBgColor | Color? | - | 未选中时背景颜色 | +| backgroundColor | Color? | - | 背景颜色 (可选) | +| centerDistance | double? | - | icon与文本中间距离(可选) | +| currentIndex | int? | - | 选中的index(可选) | +| needInkWell | bool | false | 是否需要水波纹效果 | + +``` +``` + ### TDBottomTabBarPopUpBtnConfig +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| items | List | - | 选项list | +| onChanged | ValueChanged | - | 统一在 onChanged 中处理各item点击事件 | +| popUpDialogConfig | TDBottomTabBarPopUpShapeConfig? | - | 弹窗UI配置 | + +``` +``` + ### TDBottomTabBarPopUpShapeConfig +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| popUpWidth | double? | - | 弹窗宽度(不设置,默认为按钮宽度 - 20) | +| popUpitemHeight | double? | _kDefaultMenuItemHeight | 单个选项高度 所有选项等高 不设置则使用默认值 48 | +| backgroundColor | Color? | - | 弹窗背景颜色 | +| radius | double? | - | panel圆角 默认0 | +| arrowWidth | double? | - | 箭头宽度 默认13.5 | +| arrowHeight | double? | - | 箭头高度 默认8 | + +``` +``` + ### PopUpMenuItem +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| itemWidget | Widget? | - | 选项widget | +| value | String | - | 选项值 | +| alignment | AlignmentGeometry | AlignmentDirectional.center | 对齐方式 | diff --git a/tdesign-component/example/assets/api/table_api.md b/tdesign-component/example/assets/api/table_api.md new file mode 100644 index 000000000..ea947ab6a --- /dev/null +++ b/tdesign-component/example/assets/api/table_api.md @@ -0,0 +1,54 @@ +## API +### TDTableCol +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| title | String? | - | 表头标题 | +| colKey | String? | - | 列取值字段 | +| width | double? | - | 列宽 | +| fixed | TDTableColFixed? | TDTableColFixed.none | 固定列 | +| ellipsis | bool? | - | 列内容超出时是否省略 | +| ellipsisTitle | bool? | - | 列标题超出时显示省略内容 | +| cellBuilder | IndexedWidgetBuilder? | - | 自定义列 | +| align | TDTableColAlign? | TDTableColAlign.left | 列内容横向对齐方式 | +| sortable | bool? | false | 是否可排序 | +| selection | bool? | - | 行是否显示复选框,自定义列时无效 | +| selectable | SelectableFunc? | - | 当前行CheckBox是否可选,仅selection:true有效 | +| checked | RowCheckFunc? | - | 当前行是否选中 | + +``` +``` + ### TDTableEmpty +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| assetUrl | String? | - | 空状态图片 | +| text | String? | - | 空状态文字 | + +``` +``` + ### TDTable +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| bordered | bool? | - | 是否显示表格边框 | +| columns | List | - | 列配置 | +| data | List? | - | 数据源 | +| empty | TDTableEmpty? | - | 空表格呈现样式 | +| height | double? | - | 表格高度,超出后会出现滚动条 | +| rowHeight | double? | - | 行高 | +| loading | bool? | false | 加载中状态 | +| loadingWidget | Widget? | - | 自定义加载中状态 | +| showHeader | bool? | true | 是否显示表头 | +| stripe | bool? | false | 斑马纹 | +| backgroundColor | Color? | - | 表格背景色 | +| width | double? | - | 表格宽度 | +| defaultSort | String? | - | 默认排序 | +| onCellTap | OnCellTap? | - | 单元格点击事件 | +| onScroll | OnScroll? | - | 表格滚动事件 | +| onSelect | OnSelect? | - | 选中行事件 | +| onRowSelect | OnRowSelect? | - | 行选择事件 | diff --git a/tdesign-component/example/assets/api/tabs_api.md b/tdesign-component/example/assets/api/tabs_api.md new file mode 100644 index 000000000..95955ee17 --- /dev/null +++ b/tdesign-component/example/assets/api/tabs_api.md @@ -0,0 +1,65 @@ +## API +### TDTab +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| text | String? | - | 文字内容 | +| child | Widget? | - | 子widget | +| icon | Widget? | - | 图标 | +| badge | TDBadge? | - | 图标 | +| height | double? | - | tab高度 | +| contentHeight | double? | - | 中间内容高度 | +| textMargin | EdgeInsetsGeometry? | - | 中间内容宽度 | +| size | TDTabSize | TDTabSize.small | 选项卡尺寸 | +| outlineType | TDTabOutlineType | TDTabOutlineType.filled | 选项卡样式 | +| enable | bool | true | 是否可用,默认true | +| iconMargin | EdgeInsetsGeometry | const EdgeInsets.only(bottom: 4.0, right: 4.0) | 图标间距 | + +``` +``` + ### TDTabBarView +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| children | List | - | 子widget列表 | +| controller | TabController? | - | 控制器 | +| isSlideSwitch | bool | false | 是否可以滑动切换 | + +``` +``` + ### TDTabBar +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| tabs | List | - | tab数组 | +| controller | TabController? | - | tab控制器 | +| decoration | Decoration? | - | tabBar修饰 | +| backgroundColor | Color? | - | tabBar背景色,当outlineType为card时控制选中tab颜色 | +| indicatorColor | Color? | - | tabBar下标颜色 | +| indicatorWidth | double? | - | tabBar下标宽度 | +| indicatorHeight | double? | - | tabBar下标高度 | +| labelColor | Color? | - | tabBar 已选标签颜色 | +| unselectedLabelColor | Color? | - | tabBar未选标签颜色 | +| isScrollable | bool | false | 是否滚动 | +| unselectedLabelStyle | TextStyle? | - | unselectedLabel字体 | +| labelStyle | TextStyle? | - | 已选label字体 | +| width | double? | - | tabBar宽度 | +| height | double? | - | tabBar高度 | +| indicatorPadding | EdgeInsets? | - | 引导padding | +| labelPadding | EdgeInsetsGeometry? | - | tab间距 | +| indicator | Decoration? | - | 自定义引导控件 | +| physics | ScrollPhysics? | - | 自定义滑动 | +| onTap | Function(int)? | - | 点击事件 | +| outlineType | TDTabBarOutlineType | TDTabBarOutlineType.filled | 选项卡样式 | +| showIndicator | bool | false | 是否展示引导控件 | +| dividerColor | Color? | - | 分割线颜色 | +| dividerHeight | double | 0.5 | 分割线高度,小于等于0则不展示分割线 | +| selectedBgColor | Color? | - | 被选中背景色,只有outlineType为capsule时有效 | +| unSelectedBgColor | Color? | - | 未选中背景色,只有outlineType为capsule时有效 | +| tabAlignment | | - | | diff --git a/tdesign-component/example/assets/api/tag_api.md b/tdesign-component/example/assets/api/tag_api.md new file mode 100644 index 000000000..4014650ca --- /dev/null +++ b/tdesign-component/example/assets/api/tag_api.md @@ -0,0 +1,80 @@ +## API +### TDTagStyle +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | BuildContext? | - | 上下文,方便获取主题内容 | +| textColor | Color? | - | 文字颜色 | +| backgroundColor | Color? | - | 背景颜色 | +| font | Font? | - | 字体尺寸 | +| fontWeight | FontWeight? | - | 字体粗细 | +| border | double | 0 | 线框粗细 | +| borderColor | Color? | - | 边框颜色 | +| borderRadius | BorderRadiusGeometry? | - | 圆角 | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDTagStyle.generateFillStyleByTheme | 根据主题生成填充Tag样式 | +| TDTagStyle.generateOutlineStyleByTheme | 根据主题生成描边Tag样式 | +| TDTagStyle.generateDisableSelectStyle | 根据主题生成禁用Tag样式 | + +``` +``` + ### TDTag +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| text | String | text | 标签内容 | +| theme | TDTagTheme? | - | 主题 | +| icon | IconData? | - | 图标内容,可随状态改变颜色 | +| iconWidget | Widget? | - | 自定义图标内容,需自处理颜色 | +| textColor | Color? | - | 文字颜色, 优先级高于style的textColor | +| backgroundColor | Color? | - | 背景颜色, 优先级高于style的backgroundColor | +| font | Font? | - | 字体尺寸, 优先级高于style的font | +| fontWeight | FontWeight? | - | 字体粗细, 优先级高于style的fontWeight | +| style | TDTagStyle? | - | 标签样式 | +| size | TDTagSize | TDTagSize.medium | 标签大小 | +| padding | EdgeInsets? | - | 自定义模式下的间距 | +| forceVerticalCenter | bool | true | 是否强制中文文字居中 | +| isOutline | bool | false | 是否为描边类型,默认不是 | +| shape | TDTagShape | TDTagShape.square | 标签形状 | +| isLight | bool | false | 是否为浅色 | +| disable | bool | false | 是否为禁用状态 | +| needCloseIcon | bool | false | 关闭图标 | +| onCloseTap | GestureTapCallback? | - | 关闭图标点击事件 | +| overflow | TextOverflow? | - | 文字溢出处理 | +| fixedWidth | double? | - | 标签的固定宽度 | +| key | | - | | + +``` +``` + ### TDSelectTag +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| text | String | text | 标签内容 | +| theme | TDTagTheme? | - | 主题 | +| icon | IconData? | - | 图标内容,可随状态改变颜色 | +| iconWidget | Widget? | - | 自定义图标内容,需自处理颜色 | +| selectStyle | TDTagStyle? | - | 选中的标签样式 | +| unSelectStyle | TDTagStyle? | - | 未选中标签样式 | +| disableSelectStyle | TDTagStyle? | - | 不可选标签样式 | +| onSelectChanged | ValueChanged? | - | 标签点击,选中状态改变时的回调 | +| isSelected | bool | false | 是否选中 | +| disableSelect | bool | false | 是否禁用选择 | +| size | TDTagSize | TDTagSize.medium | 标签大小 | +| padding | EdgeInsets? | - | 自定义模式下的间距 | +| forceVerticalCenter | bool | true | 是否强制中文文字居中 | +| isOutline | bool | false | 是否为描边类型,默认不是 | +| shape | TDTagShape | TDTagShape.square | 标签形状 | +| isLight | bool | false | 是否为浅色 | +| needCloseIcon | bool | false | 关闭图标 | +| onCloseTap | GestureTapCallback? | - | 关闭图标点击事件 | +| fixedWidth | double? | - | 标签的固定宽度 | +| key | | - | | diff --git a/tdesign-component/example/assets/api/text_api.md b/tdesign-component/example/assets/api/text_api.md new file mode 100644 index 000000000..24e98431a --- /dev/null +++ b/tdesign-component/example/assets/api/text_api.md @@ -0,0 +1,74 @@ +## API +### TDText +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| data | null | data | 以下系统text属性,释义请参考系统[Text]中注释 | +| font | Font? | - | 字体尺寸,包含大小size和行高height | +| fontWeight | FontWeight? | - | 字体粗细 | +| fontFamily | FontFamily? | - | 字体ttf | +| textColor | Color | Colors.black | 文本颜色 | +| backgroundColor | Color? | - | 背景颜色 | +| isTextThrough | bool? | false | 是否是横线穿过样式(删除线) | +| lineThroughColor | Color? | Colors.white | 删除线颜色,对应TestStyle的decorationColor | +| package | String? | - | 字体包名 | +| style | TextStyle? | - | 自定义的TextStyle,其中指定的属性,将覆盖扩展的外层属性 | +| strutStyle | | - | | +| textAlign | | - | | +| textDirection | | - | | +| locale | | - | | +| softWrap | | - | | +| overflow | | - | | +| textScaleFactor | | - | | +| maxLines | | - | | +| semanticsLabel | | - | | +| textWidthBasis | | - | | +| textHeightBehavior | | - | | +| forceVerticalCenter | bool | false | 是否强制居中 | +| isInFontLoader | bool | false | 是否在FontLoader中使用 | +| fontFamilyUrl | String? | - | 是否禁用懒加载FontFamily的能力 | +| key | | - | | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDText.rich | 富文本构造方法 | + +``` +``` + ### TDTextSpan +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| context | | - | | +| font | | - | | +| fontWeight | | - | | +| fontFamily | | - | | +| textColor | | Colors.black | | +| isTextThrough | | false | | +| lineThroughColor | | Colors.white | | +| package | | - | | +| text | | - | | +| children | | - | | +| style | | - | | +| recognizer | | - | | +| mouseCursor | | - | | +| onEnter | | - | | +| onExit | | - | | +| semanticsLabel | | - | | + +``` +``` + ### TDTextConfiguration +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| child | | - | | +| paddingConfig | TDTextPaddingConfig? | - | forceVerticalCenter=true时,内置padding配置 | +| globalFontFamily | FontFamily? | - | 全局字体,kTextNeedGlobalFontFamily=true时生效 | diff --git a/tdesign-component/example/assets/api/textarea_api.md b/tdesign-component/example/assets/api/textarea_api.md new file mode 100644 index 000000000..e16d9a904 --- /dev/null +++ b/tdesign-component/example/assets/api/textarea_api.md @@ -0,0 +1,50 @@ +## API +### TDTextarea +#### 简介 +用于多行文本信息输入 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| width | double? | - | 输入框宽度 | +| textStyle | TextStyle? | - | 文本颜色 | +| backgroundColor | Color? | Colors.white | 输入框背景色 | +| decoration | Decoration? | - | 输入框样式(包括标签) | +| labelStyle | TextStyle? | - | 左侧标签文本样式 | +| required | bool? | - | 是否必填标志(红色*) | +| readOnly | bool? | false | 是否只读 | +| autofocus | bool? | false | 是否自动获取焦点 | +| onEditingComplete | VoidCallback? | - | 点击键盘完成按钮时触发的回调 | +| onSubmitted | ValueChanged? | - | 点击键盘完成按钮时触发的回调, 参数值为输入的内容 | +| hintText | String? | - | 提示文案 | +| inputType | TextInputType? | - | 键盘类型,数字、字母 | +| onChanged | ValueChanged? | - | 输入文本变化时回调 | +| inputFormatters | List? | - | 显示输入内容,如限制长度(LengthLimitingTextInputFormatter(6)) | +| inputDecoration | InputDecoration? | - | 自定义输入框TextField组件样式 | +| maxLines | int? | - | 最大输入行数 | +| minLines | int? | 4 | 最小输入行数 | +| focusNode | FocusNode? | - | 获取或者取消焦点使用 | +| controller | TextEditingController? | - | controller 用户获取或者赋值输入内容 | +| cursorColor | Color? | - | 游标颜色 | +| hintTextStyle | TextStyle? | - | 提示文本颜色,默认为文本颜色 | +| labelWidget | Widget? | - | label组件,支持自定义 | +| textInputBackgroundColor | Color? | - | 文本框背景色 | +| size | TDInputSize? | TDInputSize.large | 输入框规格 | +| maxLength | int? | - | 最大字数限制 | +| maxLengthEnforcement | MaxLengthEnforcement? | - | 如何执行输入长度限制 | +| allowInputOverMax | bool? | false | 超出[maxLength]之后是否还允许输入 | +| additionInfo | String? | '' | 错误提示信息 | +| additionInfoColor | Color? | - | 错误提示颜色 | +| textAlign | TextAlign? | - | 文字对齐方向 | +| label | String? | - | 输入框标题 | +| indicator | bool? | false | 否显示文本计数器,如 0/140(必须设置maxLength) | +| layout | TDTextareaLayout? | TDTextareaLayout.horizontal | 标题输入框布局方式。可选项:vertical/horizontal | +| autosize | bool? | - | 是否自动增高,值为 true 时,[maxLines]不生效 | +| labelIcon | Widget? | - | 输入框标题图标 | +| labelWidth | double? | - | 输入框标题宽度 | +| margin | EdgeInsetsGeometry? | - | 外边距 | +| padding | EdgeInsetsGeometry? | - | 内边距 | +| textareaDecoration | Decoration? | - | 输入框样式(不包括标签) | +| bordered | bool? | - | 是否显示外边框 | +| showBottomDivider | bool? | true | 边框外部下划线 | diff --git a/tdesign-component/example/assets/api/theme_api.md b/tdesign-component/example/assets/api/theme_api.md new file mode 100644 index 000000000..ad2c55215 --- /dev/null +++ b/tdesign-component/example/assets/api/theme_api.md @@ -0,0 +1,46 @@ +## API +### TDTheme +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| data | TDThemeData | - | 主题数据 | +| child | Widget | - | 子控件 | +| systemData | ThemeData? | - | Flutter系统主题数据 | +| key | | - | | + + +#### 静态方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| needMultiTheme | | bool value, | 开启多套主题功能 | +| setResourceBuilder | | required TDTDResourceBuilder delegate, bool needAlwaysBuild, | 设置资源代理, needAlwaysBuild=true:每次都会走build方法;如果全局有多个Delegate,需要区分情况去获取,则可以设置needAlwaysBuild为true,业务自己判断返回哪个delegate needAlwaysBuild=false:返回delegate为null,则每次都会走build方法,返回了 | +| defaultData | | | 获取默认主题数据,全局唯一 | +| of | | BuildContext? context, | 获取主题数据,如果未传context则获取全局唯一的默认数据, 传了context,则获取最近的主题,取不到则会获取全局唯一默认数据 | +| ofNullable | | BuildContext? context, | 获取主题数据,取不到则可空 传了context,则获取最近的主题,取不到或未传context则返回null, | + +``` +``` + ### TDThemeData +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| name | String | - | 名称 | +| colorMap | TDMap | - | 颜色 | +| fontMap | TDMap | - | 字体尺寸 | +| radiusMap | TDMap | - | 圆角 | +| fontFamilyMap | TDMap | - | 字体样式 | +| shadowMap | TDMap> | - | 阴影 | +| spacerMap | TDMap | - | 间隔 | +| refMap | TDMap | - | 映射关系 | +| extraThemeData | TDExtraThemeData? | - | 额外定义的结构 | + + +#### 静态方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| defaultData | | TDExtraThemeData? extraThemeData, | 获取默认Data,一个App里只有一个,用于没有context的地方 | +| fromJson | | required String name, required String themeJson, null recoverDefault, TDExtraThemeData? extraThemeData, | 解析配置的json文件为主题数据 | diff --git a/tdesign-component/example/assets/api/time-counter_api.md b/tdesign-component/example/assets/api/time-counter_api.md new file mode 100644 index 000000000..93d939572 --- /dev/null +++ b/tdesign-component/example/assets/api/time-counter_api.md @@ -0,0 +1,59 @@ +## API +### TDTimeCounterController +#### 简介 +倒计时组件控制器,可控制开始(`start()`)/暂停(`pause()`)/继续(`resume()`)/重置(`reset([int? time])`) +``` +``` + ### TDTimeCounterStyle +#### 简介 +计时组件样式 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| timeWidth | double? | - | 时间容器宽度 | +| timeHeight | double? | - | 时间容器高度 | +| timePadding | EdgeInsets? | - | 时间容器内边距 | +| timeMargin | EdgeInsets? | - | 时间容器外边距 | +| timeBox | BoxDecoration? | - | 时间容器装饰 | +| timeFontFamily | FontFamily? | - | 时间字体 | +| timeFontSize | double? | - | 时间字体尺寸 | +| timeFontHeight | double? | - | 时间字体行高 | +| timeFontWeight | FontWeight? | - | 时间字体粗细 | +| timeColor | Color? | - | 时间字体颜色 | +| splitFontSize | double? | - | 分隔符字体尺寸 | +| splitFontHeight | double? | - | 分隔符字体行高 | +| splitFontWeight | FontWeight? | - | 分隔符字体粗细 | +| splitColor | Color? | - | 分隔符字体颜色 | +| space | double? | - | 时间与分隔符的间隔 | + + +#### 工厂构造方法 + +| 名称 | 说明 | +| --- | --- | +| TDTimeCounterStyle.generateStyle | 生成默认样式 | + +``` +``` + ### TDTimeCounter +#### 简介 +计时组件 +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| autoStart | bool | true | 是否自动开始倒计时 | +| content | dynamic | 'default' | 'default' / Widget Function(int time) / Widget | +| format | String | 'HH:mm:ss' | 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒(分隔符必须为长度为1的非空格的字符) | +| millisecond | bool | false | 是否开启毫秒级渲染 | +| size | TDTimeCounterSize | TDTimeCounterSize.medium | 尺寸 | +| splitWithUnit | bool | false | 使用时间单位分割 | +| theme | TDTimeCounterTheme | TDTimeCounterTheme.defaultTheme | 风格 | +| time | int | - | 必需;计时时长,单位毫秒 | +| style | TDTimeCounterStyle? | - | 自定义样式,有则优先用它,没有则根据size和theme选取 | +| onChange | Function(int time)? | - | 时间变化时触发回调 | +| onFinish | VoidCallback? | - | 计时结束时触发回调 | +| direction | TDTimeCounterDirection | TDTimeCounterDirection.down | 计时方向,默认倒计时 | +| controller | TDTimeCounterController? | - | 控制器,可控制开始/暂停/继续/重置 | diff --git a/tdesign-component/example/assets/api/toast_api.md b/tdesign-component/example/assets/api/toast_api.md new file mode 100644 index 000000000..535874329 --- /dev/null +++ b/tdesign-component/example/assets/api/toast_api.md @@ -0,0 +1,15 @@ +## API +### TDToast + +#### 静态方法 + +| 名称 | 返回类型 | 参数 | 说明 | +| --- | --- | --- | --- | +| showText | | required String? text, required BuildContext context, Duration duration, int? maxLines, BoxConstraints? constraints, bool? preventTap, Widget? customWidget, Color? backgroundColor, | 普通文本Toast | +| showIconText | | required String? text, IconData? icon, IconTextDirection direction, required BuildContext context, Duration duration, bool? preventTap, Color? backgroundColor, int? maxLines, | 带图标的Toast | +| showSuccess | | required String? text, IconTextDirection direction, required BuildContext context, Duration duration, bool? preventTap, Color? backgroundColor, int? maxLines, | 成功提示Toast | +| showWarning | | required String? text, IconTextDirection direction, required BuildContext context, Duration duration, bool? preventTap, Color? backgroundColor, int? maxLines, | 警告Toast | +| showFail | | required String? text, IconTextDirection direction, required BuildContext context, Duration duration, bool? preventTap, Color? backgroundColor, int? maxLines, | 失败提示Toast | +| showLoading | | required BuildContext context, String? text, Duration duration, bool? preventTap, Widget? customWidget, Color? backgroundColor, | 带文案的加载Toast | +| showLoadingWithoutText | | required BuildContext context, String? text, Duration duration, bool? preventTap, Color? backgroundColor, | 不带文案的加载Toast | +| dismissLoading | | | 关闭加载Toast | diff --git a/tdesign-component/example/assets/api/tree-select_api.md b/tdesign-component/example/assets/api/tree-select_api.md new file mode 100644 index 000000000..1fe604a2f --- /dev/null +++ b/tdesign-component/example/assets/api/tree-select_api.md @@ -0,0 +1,25 @@ +## API +### TDSelectOption +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| label | String | - | 标签 | +| value | int | - | 值 | +| children | List | const [] | 子选项 | +| multiple | bool | false | 当前子项支持多选 | + +``` +``` + ### TDTreeSelect +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| options | List | const [] | 展示的选项列表 | +| defaultValue | List | const [] | 初始值,对应options中的value值 | +| onChange | TDTreeSelectChangeEvent? | - | 选中值发生变化 | +| multiple | bool | false | 支持多选 | +| style | TDTreeSelectStyle | TDTreeSelectStyle.normal | 一级菜单样式 | +| height | double | 336 | 高度 | diff --git a/tdesign-component/example/assets/api/upload_api.md b/tdesign-component/example/assets/api/upload_api.md new file mode 100644 index 000000000..f966036c3 --- /dev/null +++ b/tdesign-component/example/assets/api/upload_api.md @@ -0,0 +1,23 @@ +## API +### TDUpload +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| max | int | 0 | 用于控制文件上传数量,0为不限制,仅在multiple为true时有效 | +| mediaType | List | const [TDUploadMediaType.image, TDUploadMediaType.video] | 支持上传的文件类型,图片或视频 | +| sizeLimit | double? | - | 图片大小限制,单位为KB | +| onCancel | VoidCallback? | - | 监听取消上传 | +| onError | TDUploadErrorEvent? | - | 监听获取资源错误 | +| onValidate | TDUploadValidatorEvent? | - | 监听文件校验出错 | +| onClick | TDUploadClickEvent? | - | 监听点击图片位 | +| onMaxLimitReached | VoidCallback? | - | 监听文件超过最大数量 | +| files | List | - | 控制展示的文件列表 | +| onChange | TDUploadValueChangedEvent? | - | 监听添加, 删除和替换media事件 | +| multiple | bool | false | 是否多选上传,默认false | +| width | double? | 80.0 | 图片宽度 | +| height | double? | 80.0 | 图片高度 | +| type | TDUploadBoxType | TDUploadBoxType.roundedSquare | Box类型 | +| disabled | bool? | false | 是否禁用 | +| enabledReplaceType | bool? | false | 是否启用replace功能 | diff --git a/tdesign-component/example/assets/code/action_sheet._buildBadgeGridActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildBadgeGridActionSheet.txt new file mode 100644 index 000000000..1993a22e9 --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildBadgeGridActionSheet.txt @@ -0,0 +1,22 @@ + +Widget _buildBadgeGridActionSheet(BuildContext context) { + return TDButton( + text: '带徽标宫格型', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet.showGridActionSheet(context, items: [ + TDActionSheetItem(label: '微信', icon: Image.asset('assets/img/td_action_sheet_1.png'), badge: const TDBadge(TDBadgeType.message, count: 'NEW')), + TDActionSheetItem(label: '朋友圈', icon: Image.asset('assets/img/td_action_sheet_2.png')), + TDActionSheetItem(label: 'QQ', icon: Image.asset('assets/img/td_action_sheet_3.png')), + TDActionSheetItem(label: '企业微信', icon: Image.asset('assets/img/td_action_sheet_4.png')), + TDActionSheetItem(label: '收藏', icon: const IconWithBackground(icon: TDIcons.star), badge: const TDBadge(TDBadgeType.redPoint)), + TDActionSheetItem(label: '刷新', icon: const IconWithBackground(icon: TDIcons.refresh)), + TDActionSheetItem(label: '下载', icon: const IconWithBackground(icon: TDIcons.download), badge: const TDBadge(TDBadgeType.message, count: '8')), + TDActionSheetItem(label: '复制', icon: const IconWithBackground(icon: TDIcons.queue)), + ]); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildBadgeListActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildBadgeListActionSheet.txt new file mode 100644 index 000000000..a79ff339f --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildBadgeListActionSheet.txt @@ -0,0 +1,34 @@ + +Widget _buildBadgeListActionSheet(BuildContext context) { + return TDButton( + text: '带徽标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + items: [ + TDActionSheetItem( + label: '选项一', + badge: const TDBadge(TDBadgeType.redPoint), + ), + TDActionSheetItem( + label: '选项二', + badge: const TDBadge(TDBadgeType.message, count: '8'), + ), + TDActionSheetItem( + label: '选项三', + badge: const TDBadge(TDBadgeType.message, count: '99'), + ), + TDActionSheetItem( + label: '选项四', + badge: const TDBadge(TDBadgeType.message, count: '99+'), + ), + ], + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildBadgeListCenterActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildBadgeListCenterActionSheet.txt new file mode 100644 index 000000000..8dca9b66b --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildBadgeListCenterActionSheet.txt @@ -0,0 +1,31 @@ + +Widget _buildBadgeListCenterActionSheet(BuildContext context) { + return TDButton( + text: '居中带徽标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + description: '动作面板描述文字', + items: [ + TDActionSheetItem( + label: '选项一', + badge: const TDBadge(TDBadgeType.redPoint), + ), + TDActionSheetItem( + label: '选项二', + badge: const TDBadge(TDBadgeType.message, count: '8',), + ), + TDActionSheetItem( + label: '选项三', + badge: const TDBadge(TDBadgeType.message, count: '99',), + ), + ], + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildBadgeListLeftActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildBadgeListLeftActionSheet.txt new file mode 100644 index 000000000..43b6387ab --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildBadgeListLeftActionSheet.txt @@ -0,0 +1,24 @@ + +Widget _buildBadgeListLeftActionSheet(BuildContext context) { + return TDButton( + text: '左对齐带徽标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + description: '动作面板描述文字', + align: TDActionSheetAlign.left, + items: _nums + .map((e) => TDActionSheetItem( + label: '选项$e', + badge: const TDBadge(TDBadgeType.redPoint), + )) + .toList(), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildBaseGridActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildBaseGridActionSheet.txt new file mode 100644 index 000000000..2a765a70d --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildBaseGridActionSheet.txt @@ -0,0 +1,19 @@ + +Widget _buildBaseGridActionSheet(BuildContext context) { + return TDButton( + text: '常规宫格', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + theme: TDActionSheetTheme.grid, + count: 8, + items: _gridItems, + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildBaseListActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildBaseListActionSheet.txt new file mode 100644 index 000000000..3724eab2b --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildBaseListActionSheet.txt @@ -0,0 +1,17 @@ + +Widget _buildBaseListActionSheet(BuildContext context) { + return TDButton( + text: '常规列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + items: _nums.map((e) => TDActionSheetItem(label: '选项$e')).toList(), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildBaseListStateActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildBaseListStateActionSheet.txt new file mode 100644 index 000000000..65e1cf053 --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildBaseListStateActionSheet.txt @@ -0,0 +1,40 @@ + +Widget _buildBaseListStateActionSheet(BuildContext context) { + return TDButton( + text: '列表型选项状态', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + items: [ + TDActionSheetItem( + label: '默认选项', + ), + TDActionSheetItem( + label: '自定义选项', + textStyle: TextStyle( + color: TDTheme.of(context).brandNormalColor, + ), + ), + TDActionSheetItem( + label: '失效选项', + disabled: true, + ), + TDActionSheetItem( + label: '警告选项', + textStyle: const TextStyle( + color: Colors.red, + ), + ), + ], + onSelected: (item, index) { + print('选中了:${item.label}'); + }, + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildDescGridActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildDescGridActionSheet.txt new file mode 100644 index 000000000..c96e2d5d5 --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildDescGridActionSheet.txt @@ -0,0 +1,20 @@ + +Widget _buildDescGridActionSheet(BuildContext context) { + return TDButton( + text: '带描述宫格', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + theme: TDActionSheetTheme.grid, + count: 8, + description: '动作面板描述文字', + items: _gridItems, + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildDescListActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildDescListActionSheet.txt new file mode 100644 index 000000000..9c2f0971e --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildDescListActionSheet.txt @@ -0,0 +1,18 @@ + +Widget _buildDescListActionSheet(BuildContext context) { + return TDButton( + text: '带描述列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + description: '动作面板描述文字', + items: _nums.map((e) => TDActionSheetItem(label: '选项$e')).toList(), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildIconListActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildIconListActionSheet.txt new file mode 100644 index 000000000..9abf4e064 --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildIconListActionSheet.txt @@ -0,0 +1,22 @@ + +Widget _buildIconListActionSheet(BuildContext context) { + return TDButton( + text: '带图标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + items: _nums + .map((e) => TDActionSheetItem( + label: '选项$e', + icon: const Icon(TDIcons.app), + )) + .toList(), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildIconListCenterActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildIconListCenterActionSheet.txt new file mode 100644 index 000000000..4ae23339f --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildIconListCenterActionSheet.txt @@ -0,0 +1,23 @@ + +Widget _buildIconListCenterActionSheet(BuildContext context) { + return TDButton( + text: '居中带图标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + description: '动作面板描述文字', + items: _nums + .map((e) => TDActionSheetItem( + label: '选项$e', + icon: const Icon(TDIcons.app), + )) + .toList(), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildIconListLeftActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildIconListLeftActionSheet.txt new file mode 100644 index 000000000..690fcbe9c --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildIconListLeftActionSheet.txt @@ -0,0 +1,24 @@ + +Widget _buildIconListLeftActionSheet(BuildContext context) { + return TDButton( + text: '左对齐带图标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + description: '动作面板描述文字', + align: TDActionSheetAlign.left, + items: _nums + .map((e) => TDActionSheetItem( + label: '选项$e', + icon: const Icon(TDIcons.app), + )) + .toList(), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildIconListStateActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildIconListStateActionSheet.txt new file mode 100644 index 000000000..b77972fe3 --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildIconListStateActionSheet.txt @@ -0,0 +1,44 @@ + +Widget _buildIconListStateActionSheet(BuildContext context) { + return TDButton( + text: '列表型带图标状态', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + items: [ + TDActionSheetItem( + label: '默认选项', + icon: const Icon(TDIcons.app), + ), + TDActionSheetItem( + label: '自定义选项', + icon: const Icon(TDIcons.app), + textStyle: TextStyle( + color: TDTheme.of(context).brandNormalColor, + ), + ), + TDActionSheetItem( + label: '失效选项', + icon: const Icon(TDIcons.app), + disabled: true, + ), + TDActionSheetItem( + label: '警告选项', + icon: const Icon(TDIcons.app), + textStyle: const TextStyle( + color: Colors.red, + ), + ), + ], + onSelected: (item, index) { + print('选中了:${item.label}'); + }, + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildMultiScrollGridActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildMultiScrollGridActionSheet.txt new file mode 100644 index 000000000..3fba98e3d --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildMultiScrollGridActionSheet.txt @@ -0,0 +1,45 @@ + +Widget _buildMultiScrollGridActionSheet(BuildContext context) { + return TDButton( + text: '带描述多行滚动宫格', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet.showGroupActionSheet(context, items: [ + TDActionSheetItem( + label: 'Allen', + icon: Image.asset('assets/img/td_action_sheet_5.png'), + group: '分享给好友', + ), + TDActionSheetItem( + label: 'Nick', + icon: Image.asset('assets/img/td_action_sheet_6.png'), + group: '分享给好友', + ), + TDActionSheetItem( + label: 'Jacky', + icon: Image.asset('assets/img/td_action_sheet_7.png'), + group: '分享给好友', + ), + TDActionSheetItem( + label: 'Eric', + icon: Image.asset('assets/img/td_action_sheet_8.png'), + group: '分享给好友', + ), + TDActionSheetItem( + label: 'Johnsc', + icon: Image.asset('assets/img/td_action_sheet_5.png'), + group: '分享给好友', + ), + TDActionSheetItem( + label: 'Kevin', + icon: Image.asset('assets/img/td_action_sheet_6.png'), + group: '分享给好友', + ), + ..._gridItems, + ]); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildPaginationGridActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildPaginationGridActionSheet.txt new file mode 100644 index 000000000..141bdb846 --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildPaginationGridActionSheet.txt @@ -0,0 +1,38 @@ + +Widget _buildPaginationGridActionSheet(BuildContext context) { + return TDButton( + text: '带翻页宫格', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + theme: TDActionSheetTheme.grid, + count: 8, + showPagination: true, + items: [ + ..._gridItems, + TDActionSheetItem( + label: '安卓', + icon: const IconWithBackground(icon: TDIcons.logo_android), + ), + TDActionSheetItem( + label: 'Apple', + icon: const IconWithBackground(icon: TDIcons.logo_apple), + ), + TDActionSheetItem( + label: 'Chrome', + icon: const IconWithBackground(icon: TDIcons.logo_chrome), + ), + TDActionSheetItem( + label: 'Github', + icon: const IconWithBackground(icon: TDIcons.logo_github), + ), + ], + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/action_sheet._buildScrollGridActionSheet.txt b/tdesign-component/example/assets/code/action_sheet._buildScrollGridActionSheet.txt new file mode 100644 index 000000000..05c94d4cf --- /dev/null +++ b/tdesign-component/example/assets/code/action_sheet._buildScrollGridActionSheet.txt @@ -0,0 +1,42 @@ + +Widget _buildScrollGridActionSheet(BuildContext context) { + return TDButton( + text: '多行滚动宫格', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + theme: TDActionSheetTheme.grid, + count: 8, + scrollable: true, + items: [ + ..._gridItems, + TDActionSheetItem( + label: '安卓', + icon: const IconWithBackground(icon: TDIcons.logo_android), + ), + TDActionSheetItem( + label: 'Apple', + icon: const IconWithBackground(icon: TDIcons.logo_apple), + ), + TDActionSheetItem( + label: 'Chrome', + icon: const IconWithBackground(icon: TDIcons.logo_chrome), + ), + TDActionSheetItem( + label: 'Github', + icon: const IconWithBackground(icon: TDIcons.logo_github), + ), + TDActionSheetItem( + label: 'Github', + icon: const IconWithBackground(icon: TDIcons.logo_github), + ), + ], + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/avatar._buildBadgeAvatar.txt b/tdesign-component/example/assets/code/avatar._buildBadgeAvatar.txt new file mode 100644 index 000000000..6c9a1152e --- /dev/null +++ b/tdesign-component/example/assets/code/avatar._buildBadgeAvatar.txt @@ -0,0 +1,53 @@ + + Widget _buildBadgeAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + height: 51, + width: 51, + child: Stack( + alignment:Alignment.bottomLeft, + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.normal, + defaultUrl: 'assets/img/td_avatar_1.png',), + Positioned(child: TDBadge(TDBadgeType.redPoint), right: 0, top: 0) + ], + ), + ), + const SizedBox(width: 32,), + SizedBox( + height: 51, + width: 51, + child: Stack( + alignment:Alignment.bottomLeft, + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.customText, + text: 'A'), + Positioned(child: TDBadge(TDBadgeType.message,count: '8',), right: 0, top: 0) + ], + ), + ), + const SizedBox(width: 32,), + SizedBox( + width: 51, + height: 51, + child: Stack( + alignment:Alignment.bottomLeft, + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.icon,), + Positioned(child: TDBadge(TDBadgeType.message,count: '12',), right: 0, top: 0,) + ], + ), + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/avatar._buildDisplayAvatar.txt b/tdesign-component/example/assets/code/avatar._buildDisplayAvatar.txt new file mode 100644 index 000000000..63d8421ca --- /dev/null +++ b/tdesign-component/example/assets/code/avatar._buildDisplayAvatar.txt @@ -0,0 +1,15 @@ + + Widget _buildDisplayAvatar(BuildContext context){ + var assetUrl = 'assets/img/td_avatar_1.png'; + var assetUrl2 = 'assets/img/td_avatar_2.png'; + var avatarList = [assetUrl, assetUrl2, assetUrl, assetUrl2, assetUrl]; + return Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.only(left: 16), + child: TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.display, + displayText: '+5', + avatarDisplayListAsset: avatarList,), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/avatar._buildIconAvatar.txt b/tdesign-component/example/assets/code/avatar._buildIconAvatar.txt new file mode 100644 index 000000000..3d0850e17 --- /dev/null +++ b/tdesign-component/example/assets/code/avatar._buildIconAvatar.txt @@ -0,0 +1,19 @@ + + Widget _buildIconAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.icon,), + SizedBox(width: 32,), + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.icon, + shape: TDAvatarShape.square, + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/avatar._buildImageAvatar.txt b/tdesign-component/example/assets/code/avatar._buildImageAvatar.txt new file mode 100644 index 000000000..4d2ce207c --- /dev/null +++ b/tdesign-component/example/assets/code/avatar._buildImageAvatar.txt @@ -0,0 +1,20 @@ + + Widget _buildImageAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.normal, + defaultUrl: 'assets/img/td_avatar_1.png',), + SizedBox(width: 32,), + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.normal, + shape: TDAvatarShape.square, + defaultUrl: 'assets/img/td_avatar_1.png',), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/avatar._buildLargeAvatar.txt b/tdesign-component/example/assets/code/avatar._buildLargeAvatar.txt new file mode 100644 index 000000000..090d0535b --- /dev/null +++ b/tdesign-component/example/assets/code/avatar._buildLargeAvatar.txt @@ -0,0 +1,23 @@ + + Widget _buildLargeAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.large, + type: TDAvatarType.normal, + defaultUrl: 'assets/img/td_avatar_1.png',), + SizedBox(width: 32,), + TDAvatar( + size: TDAvatarSize.large, + type: TDAvatarType.customText, + text: 'A'), + SizedBox(width: 32,), + TDAvatar( + size: TDAvatarSize.large, + type: TDAvatarType.icon,), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/avatar._buildMediumAvatar.txt b/tdesign-component/example/assets/code/avatar._buildMediumAvatar.txt new file mode 100644 index 000000000..8e0dd74eb --- /dev/null +++ b/tdesign-component/example/assets/code/avatar._buildMediumAvatar.txt @@ -0,0 +1,23 @@ + + Widget _buildMediumAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.normal, + defaultUrl: 'assets/img/td_avatar_1.png',), + SizedBox(width: 48,), + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.customText, + text: 'A'), + SizedBox(width: 48,), + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.icon,), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/avatar._buildOperationAvatar.txt b/tdesign-component/example/assets/code/avatar._buildOperationAvatar.txt new file mode 100644 index 000000000..94bb2bfdb --- /dev/null +++ b/tdesign-component/example/assets/code/avatar._buildOperationAvatar.txt @@ -0,0 +1,17 @@ + + Widget _buildOperationAvatar(BuildContext context){ + var assetUrl = 'assets/img/td_avatar_1.png'; + var assetUrl2 = 'assets/img/td_avatar_2.png'; + var avatarList = [assetUrl, assetUrl2, assetUrl, assetUrl2, assetUrl]; + return Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.only(left: 16), + child: TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.operation, + avatarDisplayListAsset: avatarList, + onTap: () { + TDToast.showText('点击了操作', context: context); + }), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/avatar._buildSmallAvatar.txt b/tdesign-component/example/assets/code/avatar._buildSmallAvatar.txt new file mode 100644 index 000000000..12cb0978e --- /dev/null +++ b/tdesign-component/example/assets/code/avatar._buildSmallAvatar.txt @@ -0,0 +1,23 @@ + + Widget _buildSmallAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.small, + type: TDAvatarType.normal, + defaultUrl: 'assets/img/td_avatar_1.png',), + SizedBox(width: 56,), + TDAvatar( + size: TDAvatarSize.small, + type: TDAvatarType.customText, + text: 'A'), + SizedBox(width: 56,), + TDAvatar( + size: TDAvatarSize.small, + type: TDAvatarType.icon,), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/avatar._buildTextAvatar.txt b/tdesign-component/example/assets/code/avatar._buildTextAvatar.txt new file mode 100644 index 000000000..b80c0977f --- /dev/null +++ b/tdesign-component/example/assets/code/avatar._buildTextAvatar.txt @@ -0,0 +1,20 @@ + + Widget _buildTextAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.customText, + text: 'A'), + SizedBox(width: 32,), + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.customText, + shape: TDAvatarShape.square, + text: 'A'), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/backtop._buildCircleBackTop.txt b/tdesign-component/example/assets/code/backtop._buildCircleBackTop.txt new file mode 100644 index 000000000..448060066 --- /dev/null +++ b/tdesign-component/example/assets/code/backtop._buildCircleBackTop.txt @@ -0,0 +1,12 @@ + + Widget _buildCircleBackTop(BuildContext context) { + return getCustomButton(context, '圆形返回顶部', () { + setState(() { + showBackTop = true; + if (controller.hasClients) { + controller.jumpTo(500); + } + style = TDBackTopStyle.circle; + }); + }); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/backtop._buildHalfCircleBackTop.txt b/tdesign-component/example/assets/code/backtop._buildHalfCircleBackTop.txt new file mode 100644 index 000000000..205ef4d6f --- /dev/null +++ b/tdesign-component/example/assets/code/backtop._buildHalfCircleBackTop.txt @@ -0,0 +1,31 @@ + + Widget _buildHalfCircleBackTop(BuildContext context) { + return Column( + children: [ + getCustomButton(context, '半圆形返回顶部', () { + setState(() { + showBackTop = true; + if (controller.hasClients) { + controller.jumpTo(500); + } + style = TDBackTopStyle.halfCircle; + }); + }), + Padding( + padding: const EdgeInsets.only(left: 16, right: 16, top: 24), + child: Wrap( + spacing: 16, + runSpacing: 24, + children: [ + getDemoBox(context), + getDemoBox(context), + getDemoBox(context), + getDemoBox(context), + getDemoBox(context), + getDemoBox(context), + ], + ), + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildBubbleBadge.txt b/tdesign-component/example/assets/code/badge._buildBubbleBadge.txt new file mode 100644 index 000000000..6cabc2afc --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildBubbleBadge.txt @@ -0,0 +1,36 @@ + + Widget _buildBubbleBadge(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 67, + height: 56, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + child: const Icon(TDIcons.shop), + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.circular( + TDTheme.of(context).radiusDefault)), + height: 48, + width: 48, + ), + const Positioned( + child: TDBadge( + TDBadgeType.bubble, + count: '领积分', + ), + right: 0, + top: 0, + ) + ], + ), + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildButtonNumberBadge.txt b/tdesign-component/example/assets/code/badge._buildButtonNumberBadge.txt new file mode 100644 index 000000000..422ed4929 --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildButtonNumberBadge.txt @@ -0,0 +1,29 @@ + + Widget _buildButtonNumberBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16), + child: SizedBox( + width: 86, + height: 54, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDButton( + width: 80, + height: 48, + text: '按钮', + size: TDButtonSize.large, + ), + Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + right: 0, + top: 0, + ) + ], + ), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildCircleBadge.txt b/tdesign-component/example/assets/code/badge._buildCircleBadge.txt new file mode 100644 index 000000000..30d3e5033 --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildCircleBadge.txt @@ -0,0 +1,28 @@ + + Widget _buildCircleBadge(BuildContext context) { + return Padding( + padding: EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 48, + height: 34, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Icon(TDIcons.notification), + Positioned( + child: TDBadge( + TDBadgeType.message, + count: '16', + ), + left: 18, + bottom: 18, + ) + ], + ), + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildCustomBadge.txt b/tdesign-component/example/assets/code/badge._buildCustomBadge.txt new file mode 100644 index 000000000..dc5bb3ef6 --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildCustomBadge.txt @@ -0,0 +1,91 @@ + + Widget _buildCustomBadge(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 64, + height: 56, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + child: const Icon(TDIcons.notification), + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.circular( + TDTheme.of(context).radiusDefault)), + height: 48, + width: 48, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + right: 0, + top: 0, + ) + ], + ), + ), + const SizedBox(width: 40), + SizedBox( + width: 64, + height: 56, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + child: const Icon(TDIcons.notification), + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.circular( + TDTheme.of(context).radiusDefault)), + height: 48, + width: 48, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '0', + ), + right: 0, + top: 0, + ) + ], + ), + ), + const SizedBox(width: 40), + SizedBox( + width: 64, + height: 56, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + child: const Icon(TDIcons.notification), + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.circular( + TDTheme.of(context).radiusDefault)), + height: 48, + width: 48, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '0', + showZero: false, + ), + right: 0, + top: 0, + ) + ], + ), + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildCustomBadgeShowingNumberEight.txt b/tdesign-component/example/assets/code/badge._buildCustomBadgeShowingNumberEight.txt new file mode 100644 index 000000000..0eef08ec8 --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildCustomBadgeShowingNumberEight.txt @@ -0,0 +1,32 @@ + + Widget _buildCustomBadgeShowingNumberEight(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(top: 8, left: 16, right: 16, bottom: 8), + child: SizedBox( + width: 64, + height: 56, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + child: const Icon(TDIcons.notification), + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.circular( + TDTheme.of(context).radiusDefault)), + height: 48, + width: 48, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + right: 0, + top: 0, + ) + ], + ), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildCustomBadgeShowingNumberZero.txt b/tdesign-component/example/assets/code/badge._buildCustomBadgeShowingNumberZero.txt new file mode 100644 index 000000000..698856498 --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildCustomBadgeShowingNumberZero.txt @@ -0,0 +1,32 @@ + + Widget _buildCustomBadgeShowingNumberZero(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(top: 8, left: 16, right: 16, bottom: 8), + child: SizedBox( + width: 64, + height: 56, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + child: const Icon(TDIcons.notification), + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.circular( + TDTheme.of(context).radiusDefault)), + height: 48, + width: 48, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '0', + ), + right: 0, + top: 0, + ) + ], + ), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildCustomBadgeWithoutShowingNumberZero.txt b/tdesign-component/example/assets/code/badge._buildCustomBadgeWithoutShowingNumberZero.txt new file mode 100644 index 000000000..78f2ab4fc --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildCustomBadgeWithoutShowingNumberZero.txt @@ -0,0 +1,33 @@ + + Widget _buildCustomBadgeWithoutShowingNumberZero(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(top: 8, left: 16, right: 16, bottom: 8), + child: SizedBox( + width: 64, + height: 56, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + child: const Icon(TDIcons.notification), + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.circular( + TDTheme.of(context).radiusDefault)), + height: 48, + width: 48, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '0', + showZero: false, + ), + right: 0, + top: 0, + ) + ], + ), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildIconNumberBadge.txt b/tdesign-component/example/assets/code/badge._buildIconNumberBadge.txt new file mode 100644 index 000000000..00d9013b2 --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildIconNumberBadge.txt @@ -0,0 +1,24 @@ + + Widget _buildIconNumberBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16), + child: SizedBox( + width: 42, + height: 36, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Icon(TDIcons.notification), + Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + left: 18, + bottom: 18, + ) + ], + ), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildLargeBadge.txt b/tdesign-component/example/assets/code/badge._buildLargeBadge.txt new file mode 100644 index 000000000..fb5cdd76b --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildLargeBadge.txt @@ -0,0 +1,32 @@ + + Widget _buildLargeBadge(BuildContext context) { + return Padding( + padding: EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 68, + height: 70, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDAvatar( + size: TDAvatarSize.large, + type: TDAvatarType.icon, + ), + Positioned( + child: TDBadge( + TDBadgeType.message, + size: TDBadgeSize.large, + count: '8', + ), + left: 48, + bottom: 48, + ) + ], + ), + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildLessThanMaxCountBadge.txt b/tdesign-component/example/assets/code/badge._buildLessThanMaxCountBadge.txt new file mode 100644 index 000000000..7dd4245cf --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildLessThanMaxCountBadge.txt @@ -0,0 +1,30 @@ + + Widget _buildLessThanMaxCountBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16, right: 16, bottom: 8), + child: SizedBox( + width: 60, + height: 50, + child: Stack( + children: [ + Positioned( + left: 0, + bottom: 0, + child: Icon(TDIcons.notification), + ), + Positioned( + child: TDBadge( + TDBadgeType.square, + count: '8888', + maxCount: '9000', + size: TDBadgeSize.large, + border: TDBadgeBorder.large, + ), + left: 18, + bottom: 18, + ), + ], + ), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildMediumBadge.txt b/tdesign-component/example/assets/code/badge._buildMediumBadge.txt new file mode 100644 index 000000000..1aba16212 --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildMediumBadge.txt @@ -0,0 +1,31 @@ + + Widget _buildMediumBadge(BuildContext context) { + return Container( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 120, + height: 54, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.icon, + ), + Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + left: 36, + bottom: 36, + ) + ], + ), + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildMessageNumberBadge.txt b/tdesign-component/example/assets/code/badge._buildMessageNumberBadge.txt new file mode 100644 index 000000000..36de3237b --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildMessageNumberBadge.txt @@ -0,0 +1,27 @@ + + Widget _buildMessageNumberBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16), + child: SizedBox( + width: 54, + height: 36, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDText( + '消息', + font: TDTheme.of(context).fontBodyLarge, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + left: 28, + bottom: 18, + ) + ], + ), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildMoreThanMaxCountBadge.txt b/tdesign-component/example/assets/code/badge._buildMoreThanMaxCountBadge.txt new file mode 100644 index 000000000..7f836cddd --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildMoreThanMaxCountBadge.txt @@ -0,0 +1,30 @@ + + Widget _buildMoreThanMaxCountBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16, right: 16, bottom: 8), + child: SizedBox( + width: 60, + height: 50, + child: Stack( + children: [ + Positioned( + left: 0, + bottom: 0, + child: Icon(TDIcons.notification), + ), + Positioned( + child: TDBadge( + TDBadgeType.square, + count: '888', + maxCount: '99', + size: TDBadgeSize.large, + border: TDBadgeBorder.large, + ), + left: 18, + bottom: 18, + ), + ], + ), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildNumberBadge.txt b/tdesign-component/example/assets/code/badge._buildNumberBadge.txt new file mode 100644 index 000000000..25de33e4c --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildNumberBadge.txt @@ -0,0 +1,78 @@ + + Widget _buildNumberBadge(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 48, + height: 32, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDText( + '消息', + font: TDTheme.of(context).fontBodyLarge, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + right: 0, + top: 0, + ) + ], + ), + ), + const SizedBox( + width: 40, + ), + SizedBox( + width: 34, + height: 34, + child: Stack( + alignment: Alignment.bottomLeft, + children: const [ + Icon(TDIcons.notification), + Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + right: 0, + top: 0, + ) + ], + ), + ), + const SizedBox( + width: 40, + ), + SizedBox( + width: 86, + height: 54, + child: Stack( + alignment: Alignment.bottomLeft, + children: const [ + TDButton( + width: 80, + height: 48, + text: '按钮', + size: TDButtonSize.large, + ), + Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + right: 0, + top: 0, + ) + ], + ), + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildRedPointBadge.txt b/tdesign-component/example/assets/code/badge._buildRedPointBadge.txt new file mode 100644 index 000000000..f44849e09 --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildRedPointBadge.txt @@ -0,0 +1,70 @@ + + Widget _buildRedPointBadge(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 40, + height: 24, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDText( + '消息', + font: TDTheme.of(context).fontBodyLarge, + ), + const Positioned( + child: TDBadge(TDBadgeType.redPoint), + right: 0, + top: 0, + ) + ], + ), + ), + const SizedBox( + width: 40, + ), + SizedBox( + width: 27, + height: 27, + child: Stack( + alignment: Alignment.bottomLeft, + children: const [ + Icon(TDIcons.notification), + Positioned( + child: TDBadge(TDBadgeType.redPoint), + right: 0, + top: 0, + ) + ], + ), + ), + const SizedBox( + width: 40, + ), + SizedBox( + width: 83, + height: 51, + child: Stack( + alignment: Alignment.bottomLeft, + children: const [ + TDButton( + width: 80, + height: 48, + text: '按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + ), + Positioned( + child: TDBadge(TDBadgeType.redPoint), + right: 0, + top: 0, + ) + ], + ), + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildRedPointButtonBadge.txt b/tdesign-component/example/assets/code/badge._buildRedPointButtonBadge.txt new file mode 100644 index 000000000..5bd2ec6cd --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildRedPointButtonBadge.txt @@ -0,0 +1,27 @@ + + Widget _buildRedPointButtonBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16, right: 16), + child: SizedBox( + width: 83, + height: 48, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDButton( + width: 80, + height: 48, + text: '按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + ), + Positioned( + child: TDBadge(TDBadgeType.redPoint), + right: 0, + top: 0, + ) + ], + ), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildRedPointIconBadge.txt b/tdesign-component/example/assets/code/badge._buildRedPointIconBadge.txt new file mode 100644 index 000000000..2b1ccf711 --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildRedPointIconBadge.txt @@ -0,0 +1,21 @@ + + Widget _buildRedPointIconBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16, right: 16), + child: SizedBox( + width: 27, + height: 27, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Icon(TDIcons.notification), + Positioned( + child: TDBadge(TDBadgeType.redPoint), + right: 0, + top: 0, + ) + ], + ), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildRedPointMessageBadge.txt b/tdesign-component/example/assets/code/badge._buildRedPointMessageBadge.txt new file mode 100644 index 000000000..8638aed98 --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildRedPointMessageBadge.txt @@ -0,0 +1,24 @@ + + Widget _buildRedPointMessageBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16, right: 16), + child: SizedBox( + width: 40, + height: 24, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDText( + '消息', + font: TDTheme.of(context).fontBodyLarge, + ), + const Positioned( + child: TDBadge(TDBadgeType.redPoint), + right: 0, + top: 0, + ) + ], + ), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildSquareBadge.txt b/tdesign-component/example/assets/code/badge._buildSquareBadge.txt new file mode 100644 index 000000000..7dca078bf --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildSquareBadge.txt @@ -0,0 +1,29 @@ + + Widget _buildSquareBadge(BuildContext context) { + return Padding( + padding: EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 48, + height: 34, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Icon(TDIcons.notification), + Positioned( + child: TDBadge( + TDBadgeType.square, + border: TDBadgeBorder.small, + count: '16', + ), + left: 20, + bottom: 18, + ) + ], + ), + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/badge._buildSubscriptBadge.txt b/tdesign-component/example/assets/code/badge._buildSubscriptBadge.txt new file mode 100644 index 000000000..685d96e6b --- /dev/null +++ b/tdesign-component/example/assets/code/badge._buildSubscriptBadge.txt @@ -0,0 +1,24 @@ + + Widget _buildSubscriptBadge(BuildContext context) { + return Stack( + alignment: Alignment.topRight, + children: [ + Container( + padding: const EdgeInsets.only(left: 16), + alignment: Alignment.centerLeft, + child: TDText( + '单行标题', + textColor: TDTheme.of(context).fontGyColor1, + font: TDTheme.of(context).fontBodyLarge, + ), + color: Colors.white, + height: 48, + width: MediaQuery.of(context).size.width, + ), + const TDBadge( + TDBadgeType.subscript, + message: 'NEW', + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._allowMultipleTaps.txt b/tdesign-component/example/assets/code/bottomTabBar._allowMultipleTaps.txt new file mode 100644 index 000000000..e9aaea90e --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._allowMultipleTaps.txt @@ -0,0 +1,18 @@ + + Widget _allowMultipleTaps(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.text, useVerticalDivider: false, navigationTabs: [ + TDBottomTabBarTabConfig( + allowMultipleTaps: true, + tabText: '支持重复点击', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '不支持重复点击', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._buildCustomTopStyle.txt b/tdesign-component/example/assets/code/bottomTabBar._buildCustomTopStyle.txt new file mode 100644 index 000000000..b0b8d59a4 --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._buildCustomTopStyle.txt @@ -0,0 +1,42 @@ + + Widget _buildCustomTopStyle(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.iconText, + topBorder: const BorderSide(color: Colors.red, width: 5), + barHeight: 61, + componentType: TDBottomTabBarComponentType.normal, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + badgeConfig: BadgeConfig( + showBadge: true, + tdBadge: const TDBadge(TDBadgeType.redPoint), + badgeTopOffset: -2, + badgeRightOffset: -10, + ), + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._capsuleTabBar.txt b/tdesign-component/example/assets/code/bottomTabBar._capsuleTabBar.txt new file mode 100644 index 000000000..f526e55d0 --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._capsuleTabBar.txt @@ -0,0 +1,35 @@ + + Widget _capsuleTabBar(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.iconText, + componentType: TDBottomTabBarComponentType.label, + outlineType: TDBottomTabBarOutlineType.capsule, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._capsuleTabBarOnLongPress.txt b/tdesign-component/example/assets/code/bottomTabBar._capsuleTabBarOnLongPress.txt new file mode 100644 index 000000000..2237c8cdd --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._capsuleTabBarOnLongPress.txt @@ -0,0 +1,45 @@ + + Widget _capsuleTabBarOnLongPress(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.iconText, + componentType: TDBottomTabBarComponentType.label, + outlineType: TDBottomTabBarOutlineType.capsule, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + onLongPress: () { + print('长按了标签1'); + TDToast.showText('长按了标签1', context: context); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + onLongPress: () { + TDToast.showText('长按了标签2', context: context); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + onLongPress: () { + TDToast.showText('长按了标签3', context: context); + }, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._customBgColor.txt b/tdesign-component/example/assets/code/bottomTabBar._customBgColor.txt new file mode 100644 index 000000000..9f82d1196 --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._customBgColor.txt @@ -0,0 +1,49 @@ + + Widget _customBgColor(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + selectedBgColor: TDTheme.of(context).errorColor3, + unselectedBgColor: TDTheme.of(context).grayColor3, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._customBgTypeTabBar.txt b/tdesign-component/example/assets/code/bottomTabBar._customBgTypeTabBar.txt new file mode 100644 index 000000000..ee952245c --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._customBgTypeTabBar.txt @@ -0,0 +1,24 @@ + + Widget _customBgTypeTabBar(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.text, + backgroundColor: TDTheme.of(context).successColor6, + selectedBgColor: TDTheme.of(context).errorColor1, + unselectedBgColor: TDTheme.of(context).brandColor1, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + unselectTabTextStyle: + TextStyle(color: TDTheme.of(context).fontGyColor1), + onTap: () { + onTapTab(context, '标签1'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._expansionPanelTypeTabBar.txt b/tdesign-component/example/assets/code/bottomTabBar._expansionPanelTypeTabBar.txt new file mode 100644 index 000000000..f0d676fcb --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._expansionPanelTypeTabBar.txt @@ -0,0 +1,54 @@ + + Widget _expansionPanelTypeTabBar(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.expansionPanel, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '展开项', + onTap: () { + onTapTab(context, '展开项'); + }, + popUpButtonConfig: TDBottomTabBarPopUpBtnConfig( + popUpDialogConfig: TDBottomTabBarPopUpShapeConfig( + radius: 10, + arrowWidth: 16, + arrowHeight: 8, + ), + items: [ + '展开项一', + '展开项二', + '展开项三', + ] + .reversed + .map((e) => PopUpMenuItem( + value: e, + itemWidget: SizedBox( + //height: 30, + child: Text( + e, + style: TextStyle( + color: TDTheme.of(context).fontGyColor1, + fontSize: 16), + ), + ), + )) + .toList(), + onChanged: (v) { + TDToast.showText('点击了 $v', context: context); + })), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar.txt b/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar.txt new file mode 100644 index 000000000..c79f74700 --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar.txt @@ -0,0 +1,23 @@ + + Widget _iconTextTypeTabBar(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar3tabs.txt b/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar3tabs.txt new file mode 100644 index 000000000..fd605ff8b --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar3tabs.txt @@ -0,0 +1,31 @@ + + Widget _iconTextTypeTabBar3tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar4tabs.txt b/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar4tabs.txt new file mode 100644 index 000000000..b67ee1e5b --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar4tabs.txt @@ -0,0 +1,39 @@ + + Widget _iconTextTypeTabBar4tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar5tabs.txt b/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar5tabs.txt new file mode 100644 index 000000000..2ea9d507a --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBar5tabs.txt @@ -0,0 +1,47 @@ + + Widget _iconTextTypeTabBar5tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBarOverflow.txt b/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBarOverflow.txt new file mode 100644 index 000000000..68cc39acb --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._iconTextTypeTabBarOverflow.txt @@ -0,0 +1,39 @@ + + Widget _iconTextTypeTabBarOverflow(BuildContext context) { + final selectedIcon = Icon( + TDIcons.app, + color: TDTheme.of(context).brandNormalColor, + ); + final unSelectedIcon = Icon( + TDIcons.app, + color: TDTheme.of(context).brandNormalColor, + ); + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: selectedIcon, + unselectedIcon: unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: selectedIcon, + unselectedIcon: unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: selectedIcon, + unselectedIcon: unSelectedIcon, + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar.txt b/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar.txt new file mode 100644 index 000000000..5e08d095d --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar.txt @@ -0,0 +1,19 @@ + + Widget _iconTypeTabBar(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.icon, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }) + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar3tabs.txt b/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar3tabs.txt new file mode 100644 index 000000000..de0ff9a1f --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar3tabs.txt @@ -0,0 +1,25 @@ + + Widget _iconTypeTabBar3tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.icon, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar4tabs.txt b/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar4tabs.txt new file mode 100644 index 000000000..faf481f43 --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar4tabs.txt @@ -0,0 +1,31 @@ + + Widget _iconTypeTabBar4tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.icon, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar5tabs.txt b/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar5tabs.txt new file mode 100644 index 000000000..ed927ae2f --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._iconTypeTabBar5tabs.txt @@ -0,0 +1,37 @@ + + Widget _iconTypeTabBar5tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.icon, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._needInkWellTabBar.txt b/tdesign-component/example/assets/code/bottomTabBar._needInkWellTabBar.txt new file mode 100644 index 000000000..c0c1f257b --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._needInkWellTabBar.txt @@ -0,0 +1,31 @@ + + Widget _needInkWellTabBar(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + needInkWell: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._setCurrentIndexToTabBar.txt b/tdesign-component/example/assets/code/bottomTabBar._setCurrentIndexToTabBar.txt new file mode 100644 index 000000000..87117105f --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._setCurrentIndexToTabBar.txt @@ -0,0 +1,47 @@ + + Widget _setCurrentIndexToTabBar(BuildContext context) { + return SizedBox( + height: 200, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: PageView( + children: const [ + Center( + child: TDText('页面1,手指左滑查看页面2'), + ), + Center( + child: TDText('页面2,手指右滑查看页面1'), + ), + ], + onPageChanged: (index) { + setState(() { + // 修改选择index + currentIndex = index; + }); + }, + )), + TDBottomTabBar( + // 设置选择index + currentIndex: currentIndex, + TDBottomTabBarBasicType.icon, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }) + ]) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar.txt b/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar.txt new file mode 100644 index 000000000..44352fd3a --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar.txt @@ -0,0 +1,33 @@ + + Widget _textTypeTabBar(BuildContext context) { + var _currentIndex = 0; + void _onTapTab(BuildContext context, String tabName, int currentIndex, + int currentSelectIndex) { + print('点击了 $tabName, 当前index: $currentIndex, 当前选择index: $currentSelectIndex'); + if (currentIndex == currentSelectIndex) { + TDToast.showText('$tabName 已经被选中了', context: context); + return; + } + TDToast.showText('点击了 $tabName', context: context); + } + + return TDBottomTabBar(TDBottomTabBarBasicType.text, + currentIndex: _currentIndex, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + _onTapTab(context, '标签1', 0, _currentIndex); + _currentIndex = 0; + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + _onTapTab(context, '标签2', 1, _currentIndex); + _currentIndex = 1; + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar3tabs.txt b/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar3tabs.txt new file mode 100644 index 000000000..187233b37 --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar3tabs.txt @@ -0,0 +1,25 @@ + + Widget _textTypeTabBar3tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.text, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar4tabs.txt b/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar4tabs.txt new file mode 100644 index 000000000..84832f000 --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar4tabs.txt @@ -0,0 +1,31 @@ + + Widget _textTypeTabBar4tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.text, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar5tabs.txt b/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar5tabs.txt new file mode 100644 index 000000000..9edba6be8 --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._textTypeTabBar5tabs.txt @@ -0,0 +1,37 @@ + + Widget _textTypeTabBar5tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.text, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._weakSelectIconTabBar.txt b/tdesign-component/example/assets/code/bottomTabBar._weakSelectIconTabBar.txt new file mode 100644 index 000000000..835893af5 --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._weakSelectIconTabBar.txt @@ -0,0 +1,40 @@ + + Widget _weakSelectIconTabBar(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.icon, + componentType: TDBottomTabBarComponentType.normal, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + badgeConfig: BadgeConfig( + showBadge: true, + tdBadge: const TDBadge(TDBadgeType.redPoint), + badgeTopOffset: -2, + badgeRightOffset: -10, + ), + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._weakSelectIconTextTabBar.txt b/tdesign-component/example/assets/code/bottomTabBar._weakSelectIconTextTabBar.txt new file mode 100644 index 000000000..74edb312a --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._weakSelectIconTextTabBar.txt @@ -0,0 +1,40 @@ + + Widget _weakSelectIconTextTabBar(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.iconText, + componentType: TDBottomTabBarComponentType.normal, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + badgeConfig: BadgeConfig( + showBadge: true, + tdBadge: const TDBadge(TDBadgeType.redPoint), + badgeTopOffset: -2, + badgeRightOffset: -10, + ), + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/bottomTabBar._weakSelectTextTabBar.txt b/tdesign-component/example/assets/code/bottomTabBar._weakSelectTextTabBar.txt new file mode 100644 index 000000000..dd742c78b --- /dev/null +++ b/tdesign-component/example/assets/code/bottomTabBar._weakSelectTextTabBar.txt @@ -0,0 +1,34 @@ + + Widget _weakSelectTextTabBar(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.text, + componentType: TDBottomTabBarComponentType.normal, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + badgeConfig: BadgeConfig( + showBadge: true, + tdBadge: const TDBadge(TDBadgeType.redPoint), + badgeTopOffset: -2, + badgeRightOffset: -10, + ), + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildChildTestButton.txt b/tdesign-component/example/assets/code/button._buildChildTestButton.txt new file mode 100644 index 000000000..c58e57cb8 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildChildTestButton.txt @@ -0,0 +1,10 @@ + + Widget _buildChildTestButton(BuildContext context) { + return TDButton( + child: Container( + height: 24, + width: 24, + color: Colors.red, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildCircleButton.txt b/tdesign-component/example/assets/code/button._buildCircleButton.txt new file mode 100644 index 000000000..b760fdc4d --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildCircleButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildCircleButton(BuildContext context) { + return const TDButton( + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.circle, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildCombinationButtons.txt b/tdesign-component/example/assets/code/button._buildCombinationButtons.txt new file mode 100644 index 000000000..e5259d4e3 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildCombinationButtons.txt @@ -0,0 +1,32 @@ + + Widget _buildCombinationButtons(BuildContext context) { + return Row( + children: const [ + SizedBox( + width: 16, + ), + Expanded( + child: TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.light, + )), + SizedBox( + width: 16, + ), + Expanded( + child: TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + )), + SizedBox( + width: 16, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDangerFillButton.txt b/tdesign-component/example/assets/code/button._buildDangerFillButton.txt new file mode 100644 index 000000000..5301a688b --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDangerFillButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildDangerFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.danger, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDangerGhostButton.txt b/tdesign-component/example/assets/code/button._buildDangerGhostButton.txt new file mode 100644 index 000000000..87a73902d --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDangerGhostButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildDangerGhostButton(BuildContext context) { + return const TDButton( + text: '幽灵按钮', + size: TDButtonSize.large, + type: TDButtonType.ghost, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.danger, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDangerStrokeButton.txt b/tdesign-component/example/assets/code/button._buildDangerStrokeButton.txt new file mode 100644 index 000000000..5db11ca0d --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDangerStrokeButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildDangerStrokeButton(BuildContext context) { + return const TDButton( + text: '描边按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.danger, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDangerTextButton.txt b/tdesign-component/example/assets/code/button._buildDangerTextButton.txt new file mode 100644 index 000000000..62743cbd4 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDangerTextButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildDangerTextButton(BuildContext context) { + return const TDButton( + text: '文字按钮', + size: TDButtonSize.large, + type: TDButtonType.text, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.danger, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDefaultFillButton.txt b/tdesign-component/example/assets/code/button._buildDefaultFillButton.txt new file mode 100644 index 000000000..d84b3b02a --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDefaultFillButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildDefaultFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.defaultTheme, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDefaultGhostButton.txt b/tdesign-component/example/assets/code/button._buildDefaultGhostButton.txt new file mode 100644 index 000000000..3629ba8f3 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDefaultGhostButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildDefaultGhostButton(BuildContext context) { + return const TDButton( + text: '幽灵按钮', + size: TDButtonSize.large, + type: TDButtonType.ghost, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.defaultTheme, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDefaultStrokeButton.txt b/tdesign-component/example/assets/code/button._buildDefaultStrokeButton.txt new file mode 100644 index 000000000..111620517 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDefaultStrokeButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildDefaultStrokeButton(BuildContext context) { + return const TDButton( + text: '描边按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.defaultTheme, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDefaultTextButton.txt b/tdesign-component/example/assets/code/button._buildDefaultTextButton.txt new file mode 100644 index 000000000..123a107aa --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDefaultTextButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildDefaultTextButton(BuildContext context) { + return const TDButton( + text: '文字按钮', + size: TDButtonSize.large, + type: TDButtonType.text, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.defaultTheme, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDisableDefaultFillButton.txt b/tdesign-component/example/assets/code/button._buildDisableDefaultFillButton.txt new file mode 100644 index 000000000..ec69e646a --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDisableDefaultFillButton.txt @@ -0,0 +1,11 @@ + + TDButton _buildDisableDefaultFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.defaultTheme, + disabled: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDisableLightFillButton.txt b/tdesign-component/example/assets/code/button._buildDisableLightFillButton.txt new file mode 100644 index 000000000..03f9ce35e --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDisableLightFillButton.txt @@ -0,0 +1,11 @@ + + TDButton _buildDisableLightFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.light, + disabled: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDisablePrimaryFillButton.txt b/tdesign-component/example/assets/code/button._buildDisablePrimaryFillButton.txt new file mode 100644 index 000000000..7272a8be8 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDisablePrimaryFillButton.txt @@ -0,0 +1,11 @@ + + TDButton _buildDisablePrimaryFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + disabled: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDisablePrimaryStrokeButton.txt b/tdesign-component/example/assets/code/button._buildDisablePrimaryStrokeButton.txt new file mode 100644 index 000000000..842737bc8 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDisablePrimaryStrokeButton.txt @@ -0,0 +1,11 @@ + + TDButton _buildDisablePrimaryStrokeButton(BuildContext context) { + return const TDButton( + text: '描边按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + disabled: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildDisablePrimaryTextButton.txt b/tdesign-component/example/assets/code/button._buildDisablePrimaryTextButton.txt new file mode 100644 index 000000000..4feec3e8c --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildDisablePrimaryTextButton.txt @@ -0,0 +1,11 @@ + + TDButton _buildDisablePrimaryTextButton(BuildContext context) { + return const TDButton( + text: '文字按钮', + size: TDButtonSize.large, + type: TDButtonType.text, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + disabled: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildExtraSmallButton.txt b/tdesign-component/example/assets/code/button._buildExtraSmallButton.txt new file mode 100644 index 000000000..905a77b95 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildExtraSmallButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildExtraSmallButton(BuildContext context) { + return const TDButton( + text: '按钮28', + size: TDButtonSize.extraSmall, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildFilledButton.txt b/tdesign-component/example/assets/code/button._buildFilledButton.txt new file mode 100644 index 000000000..86a69fbbb --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildFilledButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildFilledButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.filled, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildFilledFillButton.txt b/tdesign-component/example/assets/code/button._buildFilledFillButton.txt new file mode 100644 index 000000000..0afe6666b --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildFilledFillButton.txt @@ -0,0 +1,11 @@ + + TDButton _buildFilledFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + theme: TDButtonTheme.primary, + isBlock: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildLargeButton.txt b/tdesign-component/example/assets/code/button._buildLargeButton.txt new file mode 100644 index 000000000..f71c8c91b --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildLargeButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildLargeButton(BuildContext context) { + return const TDButton( + text: '按钮48', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildLightFillButton.txt b/tdesign-component/example/assets/code/button._buildLightFillButton.txt new file mode 100644 index 000000000..178065373 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildLightFillButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildLightFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.light, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildLightStrokeButton.txt b/tdesign-component/example/assets/code/button._buildLightStrokeButton.txt new file mode 100644 index 000000000..441c8eff8 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildLightStrokeButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildLightStrokeButton(BuildContext context) { + return const TDButton( + text: '描边按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.light, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildLightTextButton.txt b/tdesign-component/example/assets/code/button._buildLightTextButton.txt new file mode 100644 index 000000000..ffec37835 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildLightTextButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildLightTextButton(BuildContext context) { + return const TDButton( + text: '文字按钮', + size: TDButtonSize.large, + type: TDButtonType.text, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.light, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildLoadingIconButton.txt b/tdesign-component/example/assets/code/button._buildLoadingIconButton.txt new file mode 100644 index 000000000..e1d4d8f29 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildLoadingIconButton.txt @@ -0,0 +1,15 @@ + + TDButton _buildLoadingIconButton(BuildContext context) { + return TDButton( + text: '加载中', + iconWidget: TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + iconColor: TDTheme.of(context).whiteColor1, + ), + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildMediumButton.txt b/tdesign-component/example/assets/code/button._buildMediumButton.txt new file mode 100644 index 000000000..c36ae08f4 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildMediumButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildMediumButton(BuildContext context) { + return const TDButton( + text: '按钮40', + size: TDButtonSize.medium, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildPrimaryFillButton.txt b/tdesign-component/example/assets/code/button._buildPrimaryFillButton.txt new file mode 100644 index 000000000..5a1e81fcd --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildPrimaryFillButton.txt @@ -0,0 +1,11 @@ + + @Demo(group: 'button') + TDButton _buildPrimaryFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildPrimaryGhostButton.txt b/tdesign-component/example/assets/code/button._buildPrimaryGhostButton.txt new file mode 100644 index 000000000..3ddd6794d --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildPrimaryGhostButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildPrimaryGhostButton(BuildContext context) { + return const TDButton( + text: '幽灵按钮', + size: TDButtonSize.large, + type: TDButtonType.ghost, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildPrimaryStrokeButton.txt b/tdesign-component/example/assets/code/button._buildPrimaryStrokeButton.txt new file mode 100644 index 000000000..300ed8340 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildPrimaryStrokeButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildPrimaryStrokeButton(BuildContext context) { + return const TDButton( + text: '描边按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildPrimaryTextButton.txt b/tdesign-component/example/assets/code/button._buildPrimaryTextButton.txt new file mode 100644 index 000000000..535357184 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildPrimaryTextButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildPrimaryTextButton(BuildContext context) { + return const TDButton( + text: '文字按钮', + size: TDButtonSize.large, + type: TDButtonType.text, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildRectangleIconButton.txt b/tdesign-component/example/assets/code/button._buildRectangleIconButton.txt new file mode 100644 index 000000000..e0b36268d --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildRectangleIconButton.txt @@ -0,0 +1,11 @@ + + TDButton _buildRectangleIconButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildRightIconButton.txt b/tdesign-component/example/assets/code/button._buildRightIconButton.txt new file mode 100644 index 000000000..67c3b491f --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildRightIconButton.txt @@ -0,0 +1,39 @@ + + Widget _buildRightIconButton(BuildContext context) { + return Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + TDButton( + text: '填充按钮', + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + iconPosition: TDButtonIconPosition.right, + ), + TDButton( + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + iconPosition: TDButtonIconPosition.right, + ), + TDButton( + text: '间距20', + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + iconPosition: TDButtonIconPosition.right, + iconTextSpacing: 20, + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildRoundButton.txt b/tdesign-component/example/assets/code/button._buildRoundButton.txt new file mode 100644 index 000000000..7e84a010c --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildRoundButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildRoundButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.round, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildSmallButton.txt b/tdesign-component/example/assets/code/button._buildSmallButton.txt new file mode 100644 index 000000000..69683c10a --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildSmallButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildSmallButton(BuildContext context) { + return const TDButton( + text: '按钮32', + size: TDButtonSize.small, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/button._buildSquareIconButton.txt b/tdesign-component/example/assets/code/button._buildSquareIconButton.txt new file mode 100644 index 000000000..ee8029dc6 --- /dev/null +++ b/tdesign-component/example/assets/code/button._buildSquareIconButton.txt @@ -0,0 +1,10 @@ + + TDButton _buildSquareIconButton(BuildContext context) { + return const TDButton( + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.square, + theme: TDButtonTheme.primary, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/calendar._buildBlock.txt b/tdesign-component/example/assets/code/calendar._buildBlock.txt new file mode 100644 index 000000000..16c9c4184 --- /dev/null +++ b/tdesign-component/example/assets/code/calendar._buildBlock.txt @@ -0,0 +1,42 @@ + +Widget _buildBlock(BuildContext context) { + final size = MediaQuery.of(context).size; + final selected = ValueNotifier>([DateTime.now().millisecondsSinceEpoch + 30 * 24 * 60 * 60 * 1000]); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + SizedBox(width: TDTheme.of(context).spacer16), + TDButton( + text: '加一个月', + size: TDButtonSize.small, + theme: TDButtonTheme.primary, + onTap: () { + selected.value = [selected.value[0] + 30 * 24 * 60 * 60 * 1000]; + }), + SizedBox(width: TDTheme.of(context).spacer16), + TDButton( + text: '减一个月', + size: TDButtonSize.small, + theme: TDButtonTheme.primary, + onTap: () { + selected.value = [selected.value[0] - 30 * 24 * 60 * 60 * 1000]; + }), + ], + ), + SizedBox(height: TDTheme.of(context).spacer16), + ValueListenableBuilder( + valueListenable: selected, + builder: (context, value, child) { + return TDCalendar( + title: '请选择日期', + value: value, + height: size.height * 0.6 + 176, + animateTo: true, + ); + }, + ), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/calendar._buildCustomCell.txt b/tdesign-component/example/assets/code/calendar._buildCustomCell.txt new file mode 100644 index 000000000..423d14aeb --- /dev/null +++ b/tdesign-component/example/assets/code/calendar._buildCustomCell.txt @@ -0,0 +1,70 @@ + +Widget _buildCustomCell(BuildContext context) { + final size = MediaQuery.of(context).size; + final selected = ValueNotifier>([DateTime.now().millisecondsSinceEpoch + 30 * 24 * 60 * 60 * 1000]); + return ValueListenableBuilder( + valueListenable: selected, + builder: (context, value, child) { + final date = DateTime.fromMillisecondsSinceEpoch(value[0]); + return TDCellGroup( + cells: [ + TDCell( + title: '自定义日期单元格', + arrow: true, + note: '${date.year}-${date.month}-${date.day}', + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + onConfirm: (value) { + print('onConfirm:$value'); + selected.value = value; + }, + onClose: () { + print('onClose'); + }, + child: TDCalendar( + title: '请选择日期', + value: value, + height: size.height * 0.6 + 176, + onCellClick: (value, type, tdate) { + print('onCellClick:$value'); + }, + onCellLongPress: (value, type, tdate) { + print('onCellLongPress:$value'); + }, + onHeaderClick: (index, week) { + print('onHeaderClick:$week'); + }, + onChange: (value) { + print('onChange:$value'); + }, + cellWidget: (context, tdate, selectType) { + if (selectType == DateSelectType.selected) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('${tdate.date.day}', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white)), + Text('文案文案', style: TextStyle(fontSize: 6, color: Colors.white)), + Text('自定义', style: TextStyle(fontSize: 12, color: Colors.white)), + ], + ); + } + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('${tdate.date.day}', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + Text('文案文案', style: TextStyle(fontSize: 8)), + Text('自定义', style: TextStyle(fontSize: 8)), + ], + ); + } + ), + ); + }, + ), + ], + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/calendar._buildSimple.txt b/tdesign-component/example/assets/code/calendar._buildSimple.txt new file mode 100644 index 000000000..e77a92521 --- /dev/null +++ b/tdesign-component/example/assets/code/calendar._buildSimple.txt @@ -0,0 +1,161 @@ + +Widget _buildSimple(BuildContext context) { + final size = MediaQuery.of(context).size; + final selected = ValueNotifier>([DateTime.now().millisecondsSinceEpoch + 30 * 24 * 60 * 60 * 1000]); + return ValueListenableBuilder( + valueListenable: selected, + builder: (context, value, child) { + final date = DateTime.fromMillisecondsSinceEpoch(value[0]); + return TDCellGroup( + cells: [ + TDCell( + title: '单个选择日历', + arrow: true, + note: '${date.year}-${date.month}-${date.day}', + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + onConfirm: (value) { + print('onConfirm:$value'); + selected.value = value; + }, + onClose: () { + print('onClose'); + }, + child: TDCalendar( + title: '请选择日期', + value: value, + height: size.height * 0.6 + 176, + onCellClick: (value, type, tdate) { + print('onCellClick:$value'); + }, + onCellLongPress: (value, type, tdate) { + print('onCellLongPress:$value'); + }, + onHeaderClick: (index, week) { + print('onHeaderClick:$week'); + }, + onChange: (value) { + print('onChange:$value'); + }, + ), + ); + }, + ), + TDCell( + title: '多个选择日历', + arrow: true, + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + child: TDCalendar( + title: '请选择日期', + type: CalendarType.multiple, + value: [DateTime.now().millisecondsSinceEpoch], + height: size.height * 0.6 + 176, + ), + ); + }, + ), + TDCell( + title: '区间选择日历', + arrow: true, + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + child: TDCalendar( + title: '请选择日期区间', + type: CalendarType.range, + value: [ + DateTime.now().millisecondsSinceEpoch, + DateTime.now().add(const Duration(days: 6)).millisecondsSinceEpoch, + ], + height: size.height * 0.6 + 176, + ), + ); + }, + ), + TDCell( + title: '单个选择日历和时间', + arrow: true, + note: '${date.year}-${date.month}-${date.day} ${date.hour}:${date.minute}', + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + onConfirm: (value) { + print('onConfirm:$value'); + selected.value = value; + }, + onClose: () { + print('onClose'); + }, + child: TDCalendar( + title: '请选择日期和时间', + value: value, + height: size.height * 0.92, + useTimePicker: true, + // pickerHeight: 100, + // pickerItemCount: 2, + onCellClick: (value, type, tdate) { + print('onCellClick:$value'); + }, + onCellLongPress: (value, type, tdate) { + print('onCellLongPress:$value'); + }, + onHeaderClick: (index, week) { + print('onHeaderClick:$week'); + }, + onChange: (value) { + print('onChange:$value'); + }, + ), + ); + }, + ), + TDCell( + title: '区间选择日历和时间', + arrow: true, + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + onConfirm: (value) { + print('onConfirm:$value'); + }, + onClose: () { + print('onClose'); + }, + child: TDCalendar( + title: '请选择日期和时间区间', + height: size.height * 0.92, + type: CalendarType.range, + value: [ + DateTime.now().millisecondsSinceEpoch, + DateTime.now().add(const Duration(days: 3)).millisecondsSinceEpoch, + ], + useTimePicker: true, + onCellClick: (value, type, tdate) { + print('onCellClick:$value'); + }, + onCellLongPress: (value, type, tdate) { + print('onCellLongPress:$value'); + }, + onHeaderClick: (index, week) { + print('onHeaderClick:$week'); + }, + onChange: (value) { + print('onChange:$value'); + }, + ), + ); + }, + ), + ], + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/calendar._buildStyle.txt b/tdesign-component/example/assets/code/calendar._buildStyle.txt new file mode 100644 index 000000000..8cfcde64c --- /dev/null +++ b/tdesign-component/example/assets/code/calendar._buildStyle.txt @@ -0,0 +1,97 @@ + +Widget _buildStyle(BuildContext context) { + final size = MediaQuery.of(context).size; + const map = { + 1: '初一', + 2: '初二', + 3: '初三', + 14: '情人节', + 15: '元宵节', + }; + return TDCellGroup( + cells: [ + TDCell( + title: '自定义文案', + arrow: true, + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + child: TDCalendar( + title: '请选择日期', + height: size.height * 0.6 + 176, + minDate: DateTime(2022, 1, 1).millisecondsSinceEpoch, + maxDate: DateTime(2022, 2, 15).millisecondsSinceEpoch, + format: (day) { + day?.suffix = '¥60'; + if (day?.date.month == 2) { + if (map.keys.contains(day?.date.day)) { + day?.suffix = '¥100'; + day?.prefix = map[day.date.day]; + day?.style = TextStyle( + fontSize: TDTheme.of(context).fontTitleMedium?.size, + height: TDTheme.of(context).fontTitleMedium?.height, + fontWeight: TDTheme.of(context).fontTitleMedium?.fontWeight, + color: TDTheme.of(context).errorColor6, + ); + if (day?.typeNotifier.value == DateSelectType.selected) { + day?.style = day.style?.copyWith(color: TDTheme.of(context).fontWhColor1); + } + } + } + return null; + }, + ), + ); + }, + ), + TDCell( + title: '自定义按钮', + arrow: true, + onClick: (cell) { + late final TDCalendarPopup calendar; + calendar = TDCalendarPopup( + context, + visible: true, + confirmBtn: Padding( + padding: EdgeInsets.symmetric(vertical: TDTheme.of(context).spacer16), + child: TDButton( + theme: TDButtonTheme.danger, + shape: TDButtonShape.round, + text: 'ok', + isBlock: true, + size: TDButtonSize.large, + onTap: () { + print(calendar.selected); + calendar.close(); + }, + ), + ), + child: TDCalendar( + title: '请选择日期', + value: [DateTime.now().millisecondsSinceEpoch], + height: size.height * 0.6 + 176, + ), + ); + }, + ), + TDCell( + title: '自定义日期区间', + arrow: true, + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + child: TDCalendar( + title: '请选择日期', + minDate: DateTime(2000, 1, 1).millisecondsSinceEpoch, + maxDate: DateTime(3000, 1, 1).millisecondsSinceEpoch, + value: [DateTime(2024, 10, 1).millisecondsSinceEpoch], + height: size.height * 0.6 + 176, + ), + ); + }, + ), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cascader._buildHorizontalCascader.txt b/tdesign-component/example/assets/code/cascader._buildHorizontalCascader.txt new file mode 100644 index 000000000..183b6534e --- /dev/null +++ b/tdesign-component/example/assets/code/cascader._buildHorizontalCascader.txt @@ -0,0 +1,26 @@ + + Widget _buildHorizontalCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, + title: '选择地址', + subTitles: ['请选择省份', '请选择城市', '请选择区/县'], + data: _data, + initialData: _initData, + theme: 'tab', onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_1 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_1, '选择地区'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cascader._buildHorizontalCompanyCascader.txt b/tdesign-component/example/assets/code/cascader._buildHorizontalCompanyCascader.txt new file mode 100644 index 000000000..ded8d4b2b --- /dev/null +++ b/tdesign-component/example/assets/code/cascader._buildHorizontalCompanyCascader.txt @@ -0,0 +1,22 @@ + + Widget _buildHorizontalCompanyCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, title: '选择部门人员', data: _data_3,isLetterSort: true, initialData: _initData_3, theme: 'tab', + onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_3 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_3 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_3, '选择部门人员'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cascader._buildHorizontalLetterCascader.txt b/tdesign-component/example/assets/code/cascader._buildHorizontalLetterCascader.txt new file mode 100644 index 000000000..38b23748d --- /dev/null +++ b/tdesign-component/example/assets/code/cascader._buildHorizontalLetterCascader.txt @@ -0,0 +1,26 @@ + + Widget _buildHorizontalLetterCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, + title: '选择地址', + data: _data_2, + initialData: _initData_2, + isLetterSort: true, + theme: 'tab', onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_2 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_2 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_2, '选择地区'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cascader._buildSelectAnyItemCascader.txt b/tdesign-component/example/assets/code/cascader._buildSelectAnyItemCascader.txt new file mode 100644 index 000000000..1e4bb187d --- /dev/null +++ b/tdesign-component/example/assets/code/cascader._buildSelectAnyItemCascader.txt @@ -0,0 +1,31 @@ + + Widget _buildSelectAnyItemCascader(BuildContext context) { + return GestureDetector( + onTap: () { + var action = (List selectData) { + if(selectData.isEmpty){ + TDToast.showText('请选择数据', context: context); + return; + } + setState(() { + var result = []; + var len = selectData.length; + _initData_6 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_1 = result.join('/'); + }); + }; + TDCascader.showMultiCascader( + context, + title: '选择地址', + data: _data, + initialData: _initData_6, + action: TDCascaderAction(onConfirm: action), + onChange: action, + ); + }, + child: _buildSelectRow(context, _selected_1, '选择地区'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cascader._buildTestVerticalCompanyCascader.txt b/tdesign-component/example/assets/code/cascader._buildTestVerticalCompanyCascader.txt new file mode 100644 index 000000000..6a1e0cf94 --- /dev/null +++ b/tdesign-component/example/assets/code/cascader._buildTestVerticalCompanyCascader.txt @@ -0,0 +1,25 @@ + + Widget _buildTestVerticalCompanyCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, + title: '选择部门人员', + data: _data_4, + initialData: _initData_5, + theme: 'step', onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_5 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_4 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_4, '选择部门人员'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cascader._buildVerticalCascader.txt b/tdesign-component/example/assets/code/cascader._buildVerticalCascader.txt new file mode 100644 index 000000000..54f9f479b --- /dev/null +++ b/tdesign-component/example/assets/code/cascader._buildVerticalCascader.txt @@ -0,0 +1,22 @@ + + Widget _buildVerticalCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, title: '选择地址', data: _data, initialData: _initData, theme: 'step', + onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_1 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_1, '选择地区'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cascader._buildVerticalCompanyCascader.txt b/tdesign-component/example/assets/code/cascader._buildVerticalCompanyCascader.txt new file mode 100644 index 000000000..3458a57d4 --- /dev/null +++ b/tdesign-component/example/assets/code/cascader._buildVerticalCompanyCascader.txt @@ -0,0 +1,22 @@ + + Widget _buildVerticalCompanyCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, title: '选择部门人员', data: _data_3,isLetterSort: true, initialData: _initData_3, theme: 'step', + onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_3 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_3 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_3, '选择部门人员'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cascader._buildVerticalLetterCascader.txt b/tdesign-component/example/assets/code/cascader._buildVerticalLetterCascader.txt new file mode 100644 index 000000000..f5cee8ff2 --- /dev/null +++ b/tdesign-component/example/assets/code/cascader._buildVerticalLetterCascader.txt @@ -0,0 +1,22 @@ + + Widget _buildVerticalLetterCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, title: '选择地址', data: _data_2, initialData: _initData_2, theme: 'step', + onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_2 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_2 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_2, '选择地区'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cascader._buildVerticalSubTitleCascader.txt b/tdesign-component/example/assets/code/cascader._buildVerticalSubTitleCascader.txt new file mode 100644 index 000000000..703f117df --- /dev/null +++ b/tdesign-component/example/assets/code/cascader._buildVerticalSubTitleCascader.txt @@ -0,0 +1,26 @@ + + Widget _buildVerticalSubTitleCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, + title: '选择地址', + subTitles: ['请选择省份', '请选择城市', '请选择区/县'], + data: _data, + initialData: _initData_4, + theme: 'tab', onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_4 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_1 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_1, '选择地区'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cell._buildCard.txt b/tdesign-component/example/assets/code/cell._buildCard.txt new file mode 100644 index 000000000..b4f3dca99 --- /dev/null +++ b/tdesign-component/example/assets/code/cell._buildCard.txt @@ -0,0 +1,11 @@ + +Widget _buildCard(BuildContext context) { + return const TDCellGroup( + theme: TDCellGroupTheme.cardTheme, + cells: [ + TDCell(arrow: true, title: '单行标题'), + TDCell(arrow: true, title: '单行标题', required: true), + TDCell(arrow: true, title: '单行标题'), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cell._buildDesSimple.txt b/tdesign-component/example/assets/code/cell._buildDesSimple.txt new file mode 100644 index 000000000..557529c2e --- /dev/null +++ b/tdesign-component/example/assets/code/cell._buildDesSimple.txt @@ -0,0 +1,52 @@ + +Widget _buildDesSimple(BuildContext context) { + return const TDCellGroup( + cells: [ + TDCell(arrow: true, title: '单行标题', description: '一段很长很长的内容文字'), + TDCell( + arrow: true, + title: '单行标题', + description: '一段很长很长的内容文字', + required: true), + TDCell( + arrow: true, + title: '单行标题', + description: '一段很长很长的内容文字', + noteWidget: TDBadge(TDBadgeType.message, count: '8')), + TDCell( + arrow: false, + title: '单行标题', + description: '一段很长很长的内容文字', + rightIconWidget: TDSwitch(isOn: true)), + TDCell( + arrow: true, title: '单行标题', description: '一段很长很长的内容文字', note: '辅助信息'), + TDCell( + arrow: true, + title: '单行标题', + description: '一段很长很长的内容文字一段很长很长的内容文字一段很长很长的内', + leftIcon: TDIcons.lock_on), + TDCell( + arrow: false, + title: '单行标题', + description: '一段很长很长的内容文字一段很长很长的内容文字一段很长很长的内'), + TDCell( + arrow: false, + title: '多行高度不定,长文本自动换行,该选项的描述是一段很长的内容', + description: '一段很长很长的内容文字一段很长很长的内容文字一段很长很长的内'), + TDCell( + arrow: true, + title: '多行带头像', + description: '一段很长很长的内容文字', + image: AssetImage('assets/img/td_avatar_1.png'), + ), + // NetworkImage('https://tdesign.gtimg.com/mobile/demos/avatar1.png')), + TDCell( + arrow: true, + title: '多行带图片', + description: '一段很长很长的内容文字', + image: AssetImage('assets/img/image.png'), + imageCircle: 8, + ), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cell._buildPadding.txt b/tdesign-component/example/assets/code/cell._buildPadding.txt new file mode 100644 index 000000000..c7edab60b --- /dev/null +++ b/tdesign-component/example/assets/code/cell._buildPadding.txt @@ -0,0 +1,18 @@ + +Widget _buildPadding(BuildContext context) { + var style = TDCellStyle(context: context); + style.padding = const EdgeInsets.all(30); + return TDCellGroup( + theme: TDCellGroupTheme.cardTheme, + cells: [ + TDCell( + arrow: true, + title: 'padding-all-30', + style: style, + onClick: (cell) { + print('padding-all-30'); + }, + ), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/cell._buildSimple.txt b/tdesign-component/example/assets/code/cell._buildSimple.txt new file mode 100644 index 000000000..11d4498e1 --- /dev/null +++ b/tdesign-component/example/assets/code/cell._buildSimple.txt @@ -0,0 +1,20 @@ + +Widget _buildSimple(BuildContext context) { + // 可统一修改样式 + var style = TDCellStyle(context: context); + return TDCellGroup( + style: style, + cells: [ + // 可单独修改样式 + TDCell(arrow: true, title: '单行标题', style: TDCellStyle.cellStyle(context)), + TDCell(arrow: true, title: '单行标题', required: true, onClick: (cell) { + print('单行标题'); + }), + const TDCell(arrow: true, title: '单行标题', noteWidget: TDBadge(TDBadgeType.message, count: '8')), + const TDCell(arrow: false, title: '单行标题', rightIconWidget: TDSwitch(isOn: true)), + const TDCell(arrow: true, title: '单行标题', note: '辅助信息'), + const TDCell(arrow: true, title: '单行标题', leftIcon: TDIcons.lock_on), + const TDCell(arrow: false, title: '单行标题'), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._checkAllSelected.txt b/tdesign-component/example/assets/code/checkbox._checkAllSelected.txt new file mode 100644 index 000000000..68fc6bf7a --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._checkAllSelected.txt @@ -0,0 +1,64 @@ + + Widget _checkAllSelected(BuildContext context) { + const itemCount = 4; + return TDCheckboxGroupContainer( + selectIds: checkIds, + passThrough: false, + controller: controller, + child: ListView.builder( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + var title = '多选'; + if(index == 0){ + title = '全选'; + return SizedBox( + height: 56, + child: TDCheckbox( + id: 'index:$index', + title: title, + customIconBuilder: (context, checked) { + var length = controller!.allChecked().length - (controller!.checked('index:0') ? 1 : 0); + var allCheck = itemCount - 1 == length; + var halfSelected = + controller != null + && !allCheck + && length > 0; + return getAllIcon(allCheck, halfSelected); + }, + onCheckBoxChanged: (checked){ + if (checked) { + controller?.toggleAll(true); + } else { + controller?.toggleAll(false); + } + }, + ), + ); + }else{ + return SizedBox( + height: index == itemCount - 1 ? null : 56, + child: TDCheckbox( + id: 'index:$index', + title: title, + subTitle: index == itemCount - 1 ? '描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息' : null, + subTitleMaxLine: 2, + onCheckBoxChanged: (checked){ + var length = controller!.allChecked().length - (controller!.checked('index:0') ? 1 : 0); + var allCheck = itemCount - 1 == length; + var halfSelected = + controller != null + && !allCheck + && length > 0; + controller!.toggle('index:0', allCheck); + getAllIcon(allCheck, halfSelected); + }, + ), + ); + } + }, + itemCount: itemCount, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._checkPosition.txt b/tdesign-component/example/assets/code/checkbox._checkPosition.txt new file mode 100644 index 000000000..f9fc0cbbe --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._checkPosition.txt @@ -0,0 +1,23 @@ + + Widget _checkPosition(BuildContext context) { + return Column( + children: [ + TDCheckboxGroupContainer( + contentDirection: TDContentDirection.right, + selectIds: const ['index:0'], + child: const TDCheckbox( + id: 'index:0', + title: '多选', + ), + ), + TDCheckboxGroupContainer( + contentDirection: TDContentDirection.left, + selectIds: const ['index:0'], + child: const TDCheckbox( + id: 'index:0', + title: '多选', + ), + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._checkStyle.txt b/tdesign-component/example/assets/code/checkbox._checkStyle.txt new file mode 100644 index 000000000..3c926f357 --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._checkStyle.txt @@ -0,0 +1,26 @@ + + Widget _checkStyle(BuildContext context) { + return Column( + children: [ + TDCheckboxGroupContainer( + style: TDCheckboxStyle.check, + selectIds: const ['index:0'], + child: const TDCheckbox( + id: 'index:0', + title: '多选', + ), + ), + const SizedBox( + height: 17, + ), + TDCheckboxGroupContainer( + style: TDCheckboxStyle.square, + selectIds: const ['index:0'], + child: const TDCheckbox( + id: 'index:0', + title: '多选', + ), + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._checkboxStatus.txt b/tdesign-component/example/assets/code/checkbox._checkboxStatus.txt new file mode 100644 index 000000000..5551dcf6d --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._checkboxStatus.txt @@ -0,0 +1,23 @@ + + Widget _checkboxStatus(BuildContext context) { + return TDCheckboxGroupContainer( + contentDirection: TDContentDirection.right, + selectIds: const ['0'], + child: Column( + children: const [ + TDCheckbox( + id: '0', + title: '选项禁用-已选', + style: TDCheckboxStyle.circle, + enable: false, + ), + TDCheckbox( + id: '1', + title: '选项禁用-默认', + style: TDCheckboxStyle.circle, + enable: false, + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._customColor.txt b/tdesign-component/example/assets/code/checkbox._customColor.txt new file mode 100644 index 000000000..082c1a89a --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._customColor.txt @@ -0,0 +1,49 @@ + + Widget _customColor(BuildContext context) { + return TDCheckboxGroupContainer( + contentDirection: TDContentDirection.right, + selectIds: const ['0'], + child: Column( + children: [ + TDCheckbox( + selectColor: TDTheme.of(context).errorColor3, + disableColor: TDTheme.of(context).errorColor1, + id: '0', + title: '选项禁用-已选', + style: TDCheckboxStyle.circle, + enable: false, + ), + TDCheckbox( + selectColor: TDTheme.of(context).errorColor3, + disableColor: TDTheme.of(context).errorColor1, + id: '1', + title: '选项禁用-默认', + style: TDCheckboxStyle.circle, + ), + + TDCheckbox( + selectColor: TDTheme.of(context).errorColor3, + disableColor: TDTheme.of(context).errorColor1, + id: 'index:0', + title: '多选', + subTitle: '描述信息', + titleMaxLine: 2, + subTitleMaxLine: 2, + cardMode: true, + ), + + TDCheckbox( + selectColor: TDTheme.of(context).errorColor3, + id: 'index:1', + title: '多选', + titleColor: Colors.green, + subTitle: '描述信息', + subTitleColor: Colors.blue, + titleMaxLine: 2, + subTitleMaxLine: 2, + cardMode: true, + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._customFont.txt b/tdesign-component/example/assets/code/checkbox._customFont.txt new file mode 100644 index 000000000..d3abbd27a --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._customFont.txt @@ -0,0 +1,39 @@ + + Widget _customFont(BuildContext context) { + return TDCheckboxGroupContainer( + contentDirection: TDContentDirection.right, + selectIds: const ['0'], + child: Column( + children: [ + TDCheckbox( + id: '0', + title: '选项禁用-已选', + subTitle: '描述文本', + style: TDCheckboxStyle.circle, + enable: false, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + TDCheckbox( + id: '1', + title: '选项禁用-默认', + subTitle: '描述文本', + style: TDCheckboxStyle.circle, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + + TDCheckbox( + id: 'index:0', + title: '多选', + subTitle: '描述信息', + titleMaxLine: 2, + subTitleMaxLine: 2, + cardMode: true, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._customIconBuildStyle.txt b/tdesign-component/example/assets/code/checkbox._customIconBuildStyle.txt new file mode 100644 index 000000000..834469d57 --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._customIconBuildStyle.txt @@ -0,0 +1,21 @@ + + Widget _customIconBuildStyle(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['index:1'], + cardMode: true, + direction: Axis.vertical, + directionalTdCheckboxes: [ + TDCheckbox( + id: 'index:0', + title: '多选', + subTitle: '描述信息', + titleMaxLine: 2, + subTitleMaxLine: 2, + cardMode: true, + customIconBuilder: (context, checked){ + return const Icon(TDIcons.app, size: 12,); + }, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._horizontalCardStyle.txt b/tdesign-component/example/assets/code/checkbox._horizontalCardStyle.txt new file mode 100644 index 000000000..5593ceaa9 --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._horizontalCardStyle.txt @@ -0,0 +1,25 @@ + + Widget _horizontalCardStyle(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['index:1'], + cardMode: true, + direction: Axis.horizontal, + directionalTdCheckboxes: const [ + TDCheckbox( + id: 'index:0', + title: '多选', + cardMode: true, + ), + TDCheckbox( + id: 'index:1', + title: '多选', + cardMode: true, + ), + TDCheckbox( + id: 'index:2', + title: '多选', + cardMode: true, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._horizontalCheckbox.txt b/tdesign-component/example/assets/code/checkbox._horizontalCheckbox.txt new file mode 100644 index 000000000..920ff9a26 --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._horizontalCheckbox.txt @@ -0,0 +1,30 @@ + + Widget _horizontalCheckbox(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['1'], + direction: Axis.horizontal, + directionalTdCheckboxes: const [ + TDCheckbox( + id: '0', + title: '多选标题', + style: TDCheckboxStyle.circle, + insetSpacing: 12, + showDivider: false, + ), + TDCheckbox( + id: '1', + title: '多选标题', + style: TDCheckboxStyle.circle, + insetSpacing: 12, + showDivider: false, + ), + TDCheckbox( + id: '2', + title: '上限四字', + style: TDCheckboxStyle.circle, + insetSpacing: 12, + showDivider: false, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._passThroughStyle.txt b/tdesign-component/example/assets/code/checkbox._passThroughStyle.txt new file mode 100644 index 000000000..6dc145c84 --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._passThroughStyle.txt @@ -0,0 +1,21 @@ + + Widget _passThroughStyle(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['index:0'], + passThrough: true, + child: ListView.builder( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + var title = '多选'; + return TDCheckbox( + id: 'index:$index', + title: title, + size: TDCheckBoxSize.large, + ); + }, + itemCount: 4, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._verticalCardStyle.txt b/tdesign-component/example/assets/code/checkbox._verticalCardStyle.txt new file mode 100644 index 000000000..8e6235c60 --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._verticalCardStyle.txt @@ -0,0 +1,42 @@ + + Widget _verticalCardStyle(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['index:1'], + cardMode: true, + direction: Axis.vertical, + directionalTdCheckboxes: const [ + TDCheckbox( + id: 'index:0', + title: '多选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDCheckbox( + id: 'index:1', + title: '多选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDCheckbox( + id: 'index:2', + title: '多选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDCheckbox( + id: 'index:3', + title: '多选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/checkbox._verticalCheckbox.txt b/tdesign-component/example/assets/code/checkbox._verticalCheckbox.txt new file mode 100644 index 000000000..7b87001d5 --- /dev/null +++ b/tdesign-component/example/assets/code/checkbox._verticalCheckbox.txt @@ -0,0 +1,29 @@ + + Widget _verticalCheckbox(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['index:1'], + child: ListView.builder( + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + var title = '多选'; + var subTitle = ''; + if (index == 2) { + title = '多选标题多行多选标题多行多选标题多行多选标题多行多选标题多行多选标题多行'; + } + if (index == 3) { + subTitle = '描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息'; + } + return TDCheckbox( + id: 'index:$index', + title: title, + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: subTitle, + ); + }, + itemCount: 4, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/collapse._buildAccordionCollapse.txt b/tdesign-component/example/assets/code/collapse._buildAccordionCollapse.txt new file mode 100644 index 000000000..3ca1dd2bf --- /dev/null +++ b/tdesign-component/example/assets/code/collapse._buildAccordionCollapse.txt @@ -0,0 +1,21 @@ + + Widget _buildAccordionCollapse(BuildContext context) { + return TDCollapse.accordion( + style: TDCollapseStyle.block, + expansionCallback: (int index, bool isExpanded) { + setState(() { + _accordionData[index].isExpanded = !isExpanded; + }); + }, + children: _accordionData.map((CollapseDataItem item) { + return TDCollapsePanel( + headerBuilder: (BuildContext context, bool isExpanded) { + return Text(item.headerValue); + }, + isExpanded: item.isExpanded, + body: const Text(randomString), + value: item.expandedValue, + ); + }).toList(), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/collapse._buildBasicCollapse.txt b/tdesign-component/example/assets/code/collapse._buildBasicCollapse.txt new file mode 100644 index 000000000..c72bdc42f --- /dev/null +++ b/tdesign-component/example/assets/code/collapse._buildBasicCollapse.txt @@ -0,0 +1,20 @@ + + Widget _buildBasicCollapse(BuildContext context) { + return TDCollapse( + style: TDCollapseStyle.block, + expansionCallback: (int index, bool isExpanded) { + setState(() { + _basicData[index].isExpanded = !isExpanded; + }); + }, + children: _basicData.map((CollapseDataItem item) { + return TDCollapsePanel( + headerBuilder: (BuildContext context, bool isExpanded) { + return Text(item.headerValue); + }, + isExpanded: item.isExpanded, + body: const Text(randomString), + ); + }).toList(), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/collapse._buildBlockStyleCollapse.txt b/tdesign-component/example/assets/code/collapse._buildBlockStyleCollapse.txt new file mode 100644 index 000000000..378f9dbcf --- /dev/null +++ b/tdesign-component/example/assets/code/collapse._buildBlockStyleCollapse.txt @@ -0,0 +1,20 @@ + + Widget _buildBlockStyleCollapse(BuildContext context) { + return TDCollapse( + style: TDCollapseStyle.block, + expansionCallback: (int index, bool isExpanded) { + setState(() { + _blockStyleData[index].isExpanded = !isExpanded; + }); + }, + children: _blockStyleData.map((CollapseDataItem item) { + return TDCollapsePanel( + headerBuilder: (BuildContext context, bool isExpanded) { + return Text(item.headerValue); + }, + isExpanded: item.isExpanded, + body: const Text(randomString), + ); + }).toList(), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/collapse._buildCardCollapse.txt b/tdesign-component/example/assets/code/collapse._buildCardCollapse.txt new file mode 100644 index 000000000..50639ce2b --- /dev/null +++ b/tdesign-component/example/assets/code/collapse._buildCardCollapse.txt @@ -0,0 +1,20 @@ + + Widget _buildCardCollapse(BuildContext context) { + return TDCollapse( + style: TDCollapseStyle.card, + expansionCallback: (int index, bool isExpanded) { + setState(() { + _cardStyleData[index].isExpanded = !isExpanded; + }); + }, + children: _cardStyleData.map((CollapseDataItem item) { + return TDCollapsePanel( + headerBuilder: (BuildContext context, bool isExpanded) { + return Text(item.headerValue); + }, + isExpanded: item.isExpanded, + body: const Text(randomString), + ); + }).toList(), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/collapse._buildCollapseWithOperationText.txt b/tdesign-component/example/assets/code/collapse._buildCollapseWithOperationText.txt new file mode 100644 index 000000000..7608f5363 --- /dev/null +++ b/tdesign-component/example/assets/code/collapse._buildCollapseWithOperationText.txt @@ -0,0 +1,23 @@ + + Widget _buildCollapseWithOperationText(BuildContext context) { + return TDCollapse( + style: TDCollapseStyle.block, + expansionCallback: (int index, bool isExpanded) { + setState(() { + _blockStyleWithOpText[index].isExpanded = !isExpanded; + }); + }, + children: _blockStyleWithOpText.map((CollapseDataItem item) { + return TDCollapsePanel( + headerBuilder: (BuildContext context, bool isExpanded) { + return Text(item.headerValue); + }, + expandIconTextBuilder: (BuildContext context, bool isExpanded) { + return isExpanded ? '收起' : '展开'; + }, + isExpanded: item.isExpanded, + body: const Text(randomString), + ); + }).toList(), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildControl.txt b/tdesign-component/example/assets/code/countDown._buildControl.txt new file mode 100644 index 000000000..36c8b640e --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildControl.txt @@ -0,0 +1,60 @@ + +Widget _buildControl(BuildContext context) { + var controller = TDCountDownController(); + return Wrap( + direction: Axis.vertical, + spacing: 8, + children: [ + Wrap( + spacing: 8, + children: [ + TDButton( + text: '开始', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.start(); + }, + ), + TDButton( + text: '结束', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.reset(0); + }, + ), + TDButton( + text: '重置', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.reset(); + }, + ), + TDButton( + text: '暂停', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.pause(); + }, + ), + TDButton( + text: '继续', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.resume(); + }, + ), + ], + ), + TDCountDown( + time: 60 * 60 * 1000, + controller: controller, + autoStart: false, + ), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildCustomUnitLargeSize.txt b/tdesign-component/example/assets/code/countDown._buildCustomUnitLargeSize.txt new file mode 100644 index 000000000..8977e34ad --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildCustomUnitLargeSize.txt @@ -0,0 +1,10 @@ + +TDCountDown _buildCustomUnitLargeSize(BuildContext context) { + var style = TDCountDownStyle.generateStyle(context, size: TDCountDownSize.large); + style.timeColor = TDTheme.of(context).errorColor6; + return TDCountDown( + time: 60 * 60 * 1000, + splitWithUnit: true, + style: style, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildCustomUnitMediumSize.txt b/tdesign-component/example/assets/code/countDown._buildCustomUnitMediumSize.txt new file mode 100644 index 000000000..881ae4726 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildCustomUnitMediumSize.txt @@ -0,0 +1,10 @@ + +TDCountDown _buildCustomUnitMediumSize(BuildContext context) { + var style = TDCountDownStyle.generateStyle(context, size: TDCountDownSize.medium); + style.timeColor = TDTheme.of(context).errorColor6; + return TDCountDown( + time: 60 * 60 * 1000, + splitWithUnit: true, + style: style, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildCustomUnitSimple.txt b/tdesign-component/example/assets/code/countDown._buildCustomUnitSimple.txt new file mode 100644 index 000000000..1d797faa0 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildCustomUnitSimple.txt @@ -0,0 +1,6 @@ + +TDCountDown _buildCustomUnitSimple(BuildContext context) { + var style = TDCountDownStyle.generateStyle(context); + style.timeColor = TDTheme.of(context).errorColor6; + return TDCountDown(time: 60 * 60 * 1000, splitWithUnit: true, style: style); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildCustomUnitSmallSize.txt b/tdesign-component/example/assets/code/countDown._buildCustomUnitSmallSize.txt new file mode 100644 index 000000000..32d68cc91 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildCustomUnitSmallSize.txt @@ -0,0 +1,10 @@ + +TDCountDown _buildCustomUnitSmallSize(BuildContext context) { + var style = TDCountDownStyle.generateStyle(context, size: TDCountDownSize.small); + style.timeColor = TDTheme.of(context).errorColor6; + return TDCountDown( + time: 60 * 60 * 1000, + splitWithUnit: true, + style: style, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildLargeSize.txt b/tdesign-component/example/assets/code/countDown._buildLargeSize.txt new file mode 100644 index 000000000..06769eb07 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildLargeSize.txt @@ -0,0 +1,7 @@ + +TDCountDown _buildLargeSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.large, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildMediumSize.txt b/tdesign-component/example/assets/code/countDown._buildMediumSize.txt new file mode 100644 index 000000000..5e93a781c --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildMediumSize.txt @@ -0,0 +1,7 @@ + +TDCountDown _buildMediumSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.medium, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildMillisecondSimple.txt b/tdesign-component/example/assets/code/countDown._buildMillisecondSimple.txt new file mode 100644 index 000000000..eca2686d2 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildMillisecondSimple.txt @@ -0,0 +1,4 @@ + +TDCountDown _buildMillisecondSimple(BuildContext context) { + return const TDCountDown(time: 60 * 60 * 1000, millisecond: true); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildRoundLargeSize.txt b/tdesign-component/example/assets/code/countDown._buildRoundLargeSize.txt new file mode 100644 index 000000000..067af734d --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildRoundLargeSize.txt @@ -0,0 +1,8 @@ + +TDCountDown _buildRoundLargeSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.large, + theme: TDCountDownTheme.round, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildRoundMediumSize.txt b/tdesign-component/example/assets/code/countDown._buildRoundMediumSize.txt new file mode 100644 index 000000000..1be69e473 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildRoundMediumSize.txt @@ -0,0 +1,8 @@ + +TDCountDown _buildRoundMediumSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.medium, + theme: TDCountDownTheme.round, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildRoundSimple.txt b/tdesign-component/example/assets/code/countDown._buildRoundSimple.txt new file mode 100644 index 000000000..6d3b40595 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildRoundSimple.txt @@ -0,0 +1,4 @@ + +TDCountDown _buildRoundSimple(BuildContext context) { + return const TDCountDown(time: 60 * 60 * 1000, theme: TDCountDownTheme.round); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildRoundSmallSize.txt b/tdesign-component/example/assets/code/countDown._buildRoundSmallSize.txt new file mode 100644 index 000000000..214d5a1c8 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildRoundSmallSize.txt @@ -0,0 +1,8 @@ + +TDCountDown _buildRoundSmallSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.small, + theme: TDCountDownTheme.round, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildSimple.txt b/tdesign-component/example/assets/code/countDown._buildSimple.txt new file mode 100644 index 000000000..75b58012a --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildSimple.txt @@ -0,0 +1,4 @@ + +TDCountDown _buildSimple(BuildContext context) { + return const TDCountDown(time: 60 * 60 * 1000); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildSmallSize.txt b/tdesign-component/example/assets/code/countDown._buildSmallSize.txt new file mode 100644 index 000000000..0890e1aee --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildSmallSize.txt @@ -0,0 +1,7 @@ + +TDCountDown _buildSmallSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.small, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildSquareLargeSize.txt b/tdesign-component/example/assets/code/countDown._buildSquareLargeSize.txt new file mode 100644 index 000000000..4d23f28ad --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildSquareLargeSize.txt @@ -0,0 +1,8 @@ + +TDCountDown _buildSquareLargeSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.large, + theme: TDCountDownTheme.square, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildSquareMediumSize.txt b/tdesign-component/example/assets/code/countDown._buildSquareMediumSize.txt new file mode 100644 index 000000000..4405f56b6 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildSquareMediumSize.txt @@ -0,0 +1,8 @@ + +TDCountDown _buildSquareMediumSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.medium, + theme: TDCountDownTheme.square, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildSquareSimple.txt b/tdesign-component/example/assets/code/countDown._buildSquareSimple.txt new file mode 100644 index 000000000..5f33fcfb1 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildSquareSimple.txt @@ -0,0 +1,4 @@ + +TDCountDown _buildSquareSimple(BuildContext context) { + return const TDCountDown(time: 60 * 60 * 1000, theme: TDCountDownTheme.square); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildSquareSmallSize.txt b/tdesign-component/example/assets/code/countDown._buildSquareSmallSize.txt new file mode 100644 index 000000000..b50985fa7 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildSquareSmallSize.txt @@ -0,0 +1,8 @@ + +TDCountDown _buildSquareSmallSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.small, + theme: TDCountDownTheme.square, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildUnitLargeSize.txt b/tdesign-component/example/assets/code/countDown._buildUnitLargeSize.txt new file mode 100644 index 000000000..8c18c927a --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildUnitLargeSize.txt @@ -0,0 +1,9 @@ + +TDCountDown _buildUnitLargeSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.large, + theme: TDCountDownTheme.square, + splitWithUnit: true, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildUnitMediumSize.txt b/tdesign-component/example/assets/code/countDown._buildUnitMediumSize.txt new file mode 100644 index 000000000..1e4a748e6 --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildUnitMediumSize.txt @@ -0,0 +1,9 @@ + +TDCountDown _buildUnitMediumSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.medium, + theme: TDCountDownTheme.square, + splitWithUnit: true, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildUnitSimple.txt b/tdesign-component/example/assets/code/countDown._buildUnitSimple.txt new file mode 100644 index 000000000..d2654635d --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildUnitSimple.txt @@ -0,0 +1,4 @@ + +TDCountDown _buildUnitSimple(BuildContext context) { + return const TDCountDown(time: 60 * 60 * 1000, theme: TDCountDownTheme.square, splitWithUnit: true); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/countDown._buildUnitSmallSize.txt b/tdesign-component/example/assets/code/countDown._buildUnitSmallSize.txt new file mode 100644 index 000000000..248daae4f --- /dev/null +++ b/tdesign-component/example/assets/code/countDown._buildUnitSmallSize.txt @@ -0,0 +1,9 @@ + +TDCountDown _buildUnitSmallSize(BuildContext context) { + return const TDCountDown( + time: 60 * 60 * 1000, + size: TDCountDownSize.small, + theme: TDCountDownTheme.square, + splitWithUnit: true, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker._customItems.txt b/tdesign-component/example/assets/code/datetimePicker._customItems.txt new file mode 100644 index 000000000..c0152670a --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker._customItems.txt @@ -0,0 +1,53 @@ + + Widget _customItems(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker( + context, + title: '选择时间', + onConfirm: (selected) { + setState(() { + selected_9 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')} ' + '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1], + filterItems: (key, nums) { + if (key == DateTypeKey.minute) { + return [0, 15, 30]; + } + return nums; + }, + itemBuilder: (context, content, colIndex, index, + itemDistanceCalculator, distance) { + return colIndex == 5 + ? TDText( + content, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: itemDistanceCalculator.calculateFontWeight( + context, distance), + fontSize: index % 2 == 0 ? 20 : 10, + color: index % 2 == 1 + ? TDTheme.of(context).fontGyColor1 + : TDTheme.of(context).successColor6, + ), + ) + : null; + }, + ); + }, + child: buildSelectRow(context, selected_9, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker._customItemsOnlyHour.txt b/tdesign-component/example/assets/code/datetimePicker._customItemsOnlyHour.txt new file mode 100644 index 000000000..c0b3e28b8 --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker._customItemsOnlyHour.txt @@ -0,0 +1,24 @@ + + Widget _customItemsOnlyHour(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker( + context, + title: '只有时分', + onConfirm: (selected) { + Navigator.of(context).pop(); + }, + useYear: false, + useMonth: false, + useDay: false, + useSecond: false, + useHour: true, + useMinute: true, + dateStart: [2025, 1, 1, 20, 0, 0], + dateEnd: [2025, 1, 1, 23, 59, 0], + initialDate: [2025, 1, 1, 22, 46, 0], + ); + }, + child: buildSelectRow(context, selected_9, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker._customLimitTime.txt b/tdesign-component/example/assets/code/datetimePicker._customLimitTime.txt new file mode 100644 index 000000000..3be6dd302 --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker._customLimitTime.txt @@ -0,0 +1,25 @@ + + Widget _customLimitTime(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_4 = '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useYear: false, + useMonth: false, + useDay: false, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [2023, 12, 31], + dateEnd: [2023, 12, 31, 4, 12, 20], + initialDate: [2023, 12, 31, 3, 02, 03]); + }, + child: buildSelectRow(context, selected_4, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker._customSelectWidget.txt b/tdesign-component/example/assets/code/datetimePicker._customSelectWidget.txt new file mode 100644 index 000000000..2cc1a9cab --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker._customSelectWidget.txt @@ -0,0 +1,31 @@ + + Widget _customSelectWidget(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_9 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')} ' + '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1], + customSelectWidget: Container( + height: 40, + decoration: const BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.all(Radius.circular(6))), + )); + }, + child: buildSelectRow(context, selected_9, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker._customStartTime.txt b/tdesign-component/example/assets/code/datetimePicker._customStartTime.txt new file mode 100644 index 000000000..d433d71f7 --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker._customStartTime.txt @@ -0,0 +1,28 @@ + + Widget _customStartTime(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_5 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')} ' + '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useYear: true, + useMonth: true, + useDay: true, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [2012, 1, 15, 12, 28, 11], + dateEnd: [2012, 6, 15, 12, 48, 32], + initialDate: [2012, 1, 15, 13, 20]); + }, + child: buildSelectRow(context, selected_5, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker.buildAll.txt b/tdesign-component/example/assets/code/datetimePicker.buildAll.txt new file mode 100644 index 000000000..73d3cd63b --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker.buildAll.txt @@ -0,0 +1,25 @@ + + Widget buildAll(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_5 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')} ' + '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_5, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker.buildHourMinuteSecond.txt b/tdesign-component/example/assets/code/datetimePicker.buildHourMinuteSecond.txt new file mode 100644 index 000000000..33e8eed94 --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker.buildHourMinuteSecond.txt @@ -0,0 +1,25 @@ + + Widget buildHourMinuteSecond(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_4 = '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useYear: false, + useMonth: false, + useDay: false, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31, 4, 12, 20], + initialDate: [2023, 12, 31]); + }, + child: buildSelectRow(context, selected_4, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker.buildMonthDay.txt b/tdesign-component/example/assets/code/datetimePicker.buildMonthDay.txt new file mode 100644 index 000000000..33ca36795 --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker.buildMonthDay.txt @@ -0,0 +1,19 @@ + + Widget buildMonthDay(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_3 = '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useYear: false, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_3, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker.buildWeekDay.txt b/tdesign-component/example/assets/code/datetimePicker.buildWeekDay.txt new file mode 100644 index 000000000..a37521fe2 --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker.buildWeekDay.txt @@ -0,0 +1,21 @@ + + Widget buildWeekDay(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_6 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')} ' + '${weekDayList[selected['weekDay']! - 1]}'; + }); + Navigator.of(context).pop(); + }, + useWeekDay: true, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_6, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker.buildWithTitle.txt b/tdesign-component/example/assets/code/datetimePicker.buildWithTitle.txt new file mode 100644 index 000000000..288f717b9 --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker.buildWithTitle.txt @@ -0,0 +1,19 @@ + + Widget buildWithTitle(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_7 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_7, '带标题时间选择器'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker.buildWithoutTitle.txt b/tdesign-component/example/assets/code/datetimePicker.buildWithoutTitle.txt new file mode 100644 index 000000000..91cd82ef0 --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker.buildWithoutTitle.txt @@ -0,0 +1,19 @@ + + Widget buildWithoutTitle(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '', onConfirm: (selected) { + setState(() { + selected_8 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_8, '无标题时间选择器'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker.buildYearMonth.txt b/tdesign-component/example/assets/code/datetimePicker.buildYearMonth.txt new file mode 100644 index 000000000..ea6276b8c --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker.buildYearMonth.txt @@ -0,0 +1,19 @@ + + Widget buildYearMonth(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_2 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useDay: false, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_2, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/datetimePicker.buildYearMonthDay.txt b/tdesign-component/example/assets/code/datetimePicker.buildYearMonthDay.txt new file mode 100644 index 000000000..a46ea5892 --- /dev/null +++ b/tdesign-component/example/assets/code/datetimePicker.buildYearMonthDay.txt @@ -0,0 +1,18 @@ + + Widget buildYearMonthDay(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_1 = + '${selected['year'].toString().padLeft(4, '0')}-${selected['month'].toString().padLeft(2, '0')}-${selected['day'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_1, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildConfirmNoTitle.txt b/tdesign-component/example/assets/code/dialog._buildConfirmNoTitle.txt new file mode 100644 index 000000000..42d26c2d2 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildConfirmNoTitle.txt @@ -0,0 +1,20 @@ + + Widget _buildConfirmNoTitle(BuildContext context) { + return TDButton( + text: '确认类-无标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + content: _commonContent, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildConfirmNormal.txt b/tdesign-component/example/assets/code/dialog._buildConfirmNormal.txt new file mode 100644 index 000000000..4496469a8 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildConfirmNormal.txt @@ -0,0 +1,21 @@ + + Widget _buildConfirmNormal(BuildContext context) { + return TDButton( + text: '确认类-带标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + title: _dialogTitle, + content: _commonContent, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildConfirmOnlyTitle.txt b/tdesign-component/example/assets/code/dialog._buildConfirmOnlyTitle.txt new file mode 100644 index 000000000..066a5a361 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildConfirmOnlyTitle.txt @@ -0,0 +1,20 @@ + + Widget _buildConfirmOnlyTitle(BuildContext context) { + return TDButton( + text: '确认类-纯标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + title: _dialogTitle, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildDialogWithCloseButton.txt b/tdesign-component/example/assets/code/dialog._buildDialogWithCloseButton.txt new file mode 100644 index 000000000..e0e678a9e --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildDialogWithCloseButton.txt @@ -0,0 +1,22 @@ + + Widget _buildDialogWithCloseButton(BuildContext context) { + return TDButton( + text: '带关闭按钮的对话框', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _commonContent, + showCloseButton: true, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildFeedbackLongContent.txt b/tdesign-component/example/assets/code/dialog._buildFeedbackLongContent.txt new file mode 100644 index 000000000..758fa2d73 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildFeedbackLongContent.txt @@ -0,0 +1,22 @@ + + Widget _buildFeedbackLongContent(BuildContext context) { + return TDButton( + text: '反馈类-内容超长', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _longContent, + contentMaxHeight: 300, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildFeedbackNoTitle.txt b/tdesign-component/example/assets/code/dialog._buildFeedbackNoTitle.txt new file mode 100644 index 000000000..be0cebdb8 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildFeedbackNoTitle.txt @@ -0,0 +1,20 @@ + + Widget _buildFeedbackNoTitle(BuildContext context) { + return TDButton( + text: '反馈类-无标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + content: _commonContent, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildFeedbackNormal.txt b/tdesign-component/example/assets/code/dialog._buildFeedbackNormal.txt new file mode 100644 index 000000000..27fff275c --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildFeedbackNormal.txt @@ -0,0 +1,21 @@ + + Widget _buildFeedbackNormal(BuildContext context) { + return TDButton( + text: '反馈类-带标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _commonContent, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildFeedbackOnlyTitle.txt b/tdesign-component/example/assets/code/dialog._buildFeedbackOnlyTitle.txt new file mode 100644 index 000000000..fbf2b5db1 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildFeedbackOnlyTitle.txt @@ -0,0 +1,20 @@ + + Widget _buildFeedbackOnlyTitle(BuildContext context) { + return TDButton( + text: '反馈类-纯标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildImageMiddle.txt b/tdesign-component/example/assets/code/dialog._buildImageMiddle.txt new file mode 100644 index 000000000..599fcb2bd --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildImageMiddle.txt @@ -0,0 +1,23 @@ + + Widget _buildImageMiddle(BuildContext context) { + return TDButton( + text: '图片居中-带标题描述', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + title: _dialogTitle, + content: _commonContent, + imagePosition: TDDialogImagePosition.middle, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildImageMiddleOnlyImage.txt b/tdesign-component/example/assets/code/dialog._buildImageMiddleOnlyImage.txt new file mode 100644 index 000000000..ae660a27f --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildImageMiddleOnlyImage.txt @@ -0,0 +1,21 @@ + + Widget _buildImageMiddleOnlyImage(BuildContext context) { + return TDButton( + text: '图片居中-纯图片', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + imagePosition: TDDialogImagePosition.middle, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildImageMiddleOnlyTitle.txt b/tdesign-component/example/assets/code/dialog._buildImageMiddleOnlyTitle.txt new file mode 100644 index 000000000..0c713bc0d --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildImageMiddleOnlyTitle.txt @@ -0,0 +1,22 @@ + + Widget _buildImageMiddleOnlyTitle(BuildContext context) { + return TDButton( + text: '图片居中-纯标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + title: _dialogTitle, + imagePosition: TDDialogImagePosition.middle, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildImageTop.txt b/tdesign-component/example/assets/code/dialog._buildImageTop.txt new file mode 100644 index 000000000..5acee2c82 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildImageTop.txt @@ -0,0 +1,22 @@ + + Widget _buildImageTop(BuildContext context) { + return TDButton( + text: '图片置顶-带标题描述', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + title: _dialogTitle, + content: _commonContent, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildImageTopNoTitle.txt b/tdesign-component/example/assets/code/dialog._buildImageTopNoTitle.txt new file mode 100644 index 000000000..4f1fc6ae3 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildImageTopNoTitle.txt @@ -0,0 +1,21 @@ + + Widget _buildImageTopNoTitle(BuildContext context) { + return TDButton( + text: '图片置顶-无标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + content: _commonContent, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildImageTopOnlyTitle.txt b/tdesign-component/example/assets/code/dialog._buildImageTopOnlyTitle.txt new file mode 100644 index 000000000..b4d08834d --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildImageTopOnlyTitle.txt @@ -0,0 +1,21 @@ + + Widget _buildImageTopOnlyTitle(BuildContext context) { + return TDButton( + text: '图片置顶-纯标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + title: _dialogTitle, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildInputNoContent.txt b/tdesign-component/example/assets/code/dialog._buildInputNoContent.txt new file mode 100644 index 000000000..6fdfe1ac9 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildInputNoContent.txt @@ -0,0 +1,22 @@ + + Widget _buildInputNoContent(BuildContext context) { + return TDButton( + text: '输入类-无描述', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDInputDialog( + textEditingController: TextEditingController(), + title: _dialogTitle, + hintText: _inputHint, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildInputNormal.txt b/tdesign-component/example/assets/code/dialog._buildInputNormal.txt new file mode 100644 index 000000000..5b418542d --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildInputNormal.txt @@ -0,0 +1,23 @@ + + Widget _buildInputNormal(BuildContext context) { + return TDButton( + text: '输入类-带描述', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDInputDialog( + textEditingController: TextEditingController(), + title: _dialogTitle, + content: _commonContent, + hintText: _inputHint, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildNormalButtonDouble.txt b/tdesign-component/example/assets/code/dialog._buildNormalButtonDouble.txt new file mode 100644 index 000000000..d38c9e4c4 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildNormalButtonDouble.txt @@ -0,0 +1,21 @@ + + Widget _buildNormalButtonDouble(BuildContext context) { + return TDButton( + text: '左右横向基础按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + title: _dialogTitle, + content: _commonContent, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildNormalButtonSingle.txt b/tdesign-component/example/assets/code/dialog._buildNormalButtonSingle.txt new file mode 100644 index 000000000..641af2810 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildNormalButtonSingle.txt @@ -0,0 +1,21 @@ + + Widget _buildNormalButtonSingle(BuildContext context) { + return TDButton( + text: '单个横向基础按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _commonContent, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildTextButtonDouble.txt b/tdesign-component/example/assets/code/dialog._buildTextButtonDouble.txt new file mode 100644 index 000000000..ee27a9387 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildTextButtonDouble.txt @@ -0,0 +1,22 @@ + + Widget _buildTextButtonDouble(BuildContext context) { + return TDButton( + text: '左右文字按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + title: _dialogTitle, + content: _commonContent, + buttonStyle: TDDialogButtonStyle.text, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildTextButtonSingle.txt b/tdesign-component/example/assets/code/dialog._buildTextButtonSingle.txt new file mode 100644 index 000000000..4d5e4ef0f --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildTextButtonSingle.txt @@ -0,0 +1,22 @@ + + Widget _buildTextButtonSingle(BuildContext context) { + return TDButton( + text: '单个文字按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _commonContent, + buttonStyle: TDDialogButtonStyle.text, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildVerticalButtonDouble.txt b/tdesign-component/example/assets/code/dialog._buildVerticalButtonDouble.txt new file mode 100644 index 000000000..b36a81a01 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildVerticalButtonDouble.txt @@ -0,0 +1,35 @@ + + Widget _buildVerticalButtonDouble(BuildContext context) { + return TDButton( + text: '两个纵向基础按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog.vertical( + title: _dialogTitle, + content: _commonContent, + buttons: [ + TDDialogButtonOptions( + title: '主要按钮', + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.primary), + TDDialogButtonOptions( + title: '次要按钮', + titleColor: TDTheme.of(context).brandColor7, + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.light), + ]); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._buildVerticalButtonTriple.txt b/tdesign-component/example/assets/code/dialog._buildVerticalButtonTriple.txt new file mode 100644 index 000000000..7d37fe193 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._buildVerticalButtonTriple.txt @@ -0,0 +1,42 @@ + + Widget _buildVerticalButtonTriple(BuildContext context) { + return TDButton( + text: '三个纵向基础按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog.vertical( + title: _dialogTitle, + content: _commonContent, + buttons: [ + TDDialogButtonOptions( + title: '主要按钮', + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.primary), + TDDialogButtonOptions( + title: '次要按钮', + titleColor: TDTheme.of(context).brandColor7, + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.light), + TDDialogButtonOptions( + title: '次要按钮', + titleColor: TDTheme.of(context).brandColor7, + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.light), + ]); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._customConfirmNormal.txt b/tdesign-component/example/assets/code/dialog._customConfirmNormal.txt new file mode 100644 index 000000000..fb72fff81 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._customConfirmNormal.txt @@ -0,0 +1,29 @@ + + Widget _customConfirmNormal(BuildContext context) { + return TDButton( + text: '确认类-标题偏右', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + title: _dialogTitle, + titleAlignment: Alignment.centerRight, + contentWidget: TDText.rich( + TDTextSpan( + children: [ + TDTextSpan(text: '红色文字', textColor: Colors.red), + TDTextSpan(text: '绿色文字', textColor: Colors.green), + ] + ) + ), + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._customConfirmVertical.txt b/tdesign-component/example/assets/code/dialog._customConfirmVertical.txt new file mode 100644 index 000000000..fdda4c04b --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._customConfirmVertical.txt @@ -0,0 +1,42 @@ + + Widget _customConfirmVertical(BuildContext context) { + return TDButton( + text: '纵向按钮-自定义内容', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog.vertical( + title: _dialogTitle, + contentWidget: TDText.rich( + TDTextSpan( + children: [ + TDTextSpan(text: '红色文字', textColor: Colors.red), + TDTextSpan(text: '绿色文字', textColor: Colors.green), + ] + ) + ), + buttons: [ + TDDialogButtonOptions( + title: '主要按钮', + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.primary), + TDDialogButtonOptions( + title: '次要按钮', + titleColor: TDTheme.of(context).brandColor7, + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.light), + ]); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._customContentAndBtn.txt b/tdesign-component/example/assets/code/dialog._customContentAndBtn.txt new file mode 100644 index 000000000..f12b6d4f2 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._customContentAndBtn.txt @@ -0,0 +1,32 @@ + + Widget _customContentAndBtn(BuildContext context) { + return TDButton( + text: '自定义边距和按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _commonContent, + padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), + buttonWidget: Container( + padding: const EdgeInsets.fromLTRB(0, 16, 0, 16), + child: TDButton( + text: '自定义按钮', + theme: TDButtonTheme.primary, + onTap: () { + Navigator.of(context).pop(); + }, + ), + ), + ); + } + ); + } + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._customFeedbackNormal.txt b/tdesign-component/example/assets/code/dialog._customFeedbackNormal.txt new file mode 100644 index 000000000..859f0a0d9 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._customFeedbackNormal.txt @@ -0,0 +1,29 @@ + + Widget _customFeedbackNormal(BuildContext context) { + return TDButton( + text: '反馈类-标题偏左', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + titleAlignment: Alignment.centerLeft, + contentWidget: TDText.rich( + TDTextSpan( + children: [ + TDTextSpan(text: '红色文字', textColor: Colors.red), + TDTextSpan(text: '绿色文字', textColor: Colors.green), + ] + ) + ), + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dialog._customImageTop.txt b/tdesign-component/example/assets/code/dialog._customImageTop.txt new file mode 100644 index 000000000..dd5f990b3 --- /dev/null +++ b/tdesign-component/example/assets/code/dialog._customImageTop.txt @@ -0,0 +1,28 @@ + + Widget _customImageTop(BuildContext context) { + return TDButton( + text: '图片置顶-自定义列表内容', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + title: _dialogTitle, + contentWidget: ListView( + shrinkWrap: true, + children: const [ + TDText('红色文字', textColor: Colors.red), + TDText('绿色文字', textColor: Colors.green), + ], + ), + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/divider._dashedDivider.txt b/tdesign-component/example/assets/code/divider._dashedDivider.txt new file mode 100644 index 000000000..fd1f64196 --- /dev/null +++ b/tdesign-component/example/assets/code/divider._dashedDivider.txt @@ -0,0 +1,37 @@ + + Widget _dashedDivider(BuildContext context) { + return Column( + children: const [ + SizedBox( + height: 20, + ), + TDDivider( + isDashed: true, + ), + SizedBox( + height: 20, + ), + TDDivider( + text: '文字信息', + alignment: TextAlignment.left, + isDashed: true, + ), + SizedBox( + height: 20, + ), + TDDivider( + text: '文字信息', + alignment: TextAlignment.center, + isDashed: true, + ), + SizedBox( + height: 20, + ), + TDDivider( + text: '文字信息', + alignment: TextAlignment.right, + isDashed: true, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/divider._horizontalTextDivider.txt b/tdesign-component/example/assets/code/divider._horizontalTextDivider.txt new file mode 100644 index 000000000..6c22d7704 --- /dev/null +++ b/tdesign-component/example/assets/code/divider._horizontalTextDivider.txt @@ -0,0 +1,35 @@ + + Widget _horizontalTextDivider(BuildContext context) { + return Container( + alignment: Alignment.centerLeft, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox( + width: 16, + ), + TDText( + '文字信息', + textColor: TDTheme.of(context).fontGyColor1.withOpacity(0.9), + ), + const TDDivider( + width: 0.5, + height: 12, + margin: EdgeInsets.only(left: 16, right: 16), + ), + TDText('文字信息', + textColor: TDTheme.of(context).fontGyColor1.withOpacity(0.9)), + const TDDivider( + width: 0.5, + height: 12, + margin: EdgeInsets.only(left: 16, right: 16), + isDashed: true, + direction: Axis.vertical, + ), + TDText('文字信息', + textColor: TDTheme.of(context).fontGyColor1.withOpacity(0.9)), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/divider._verticalDivider.txt b/tdesign-component/example/assets/code/divider._verticalDivider.txt new file mode 100644 index 000000000..3d0ad7197 --- /dev/null +++ b/tdesign-component/example/assets/code/divider._verticalDivider.txt @@ -0,0 +1,10 @@ + + Widget _verticalDivider(BuildContext context) { + return SizedBox( + height: 20, + child: Container( + alignment: Alignment.center, + child: const TDDivider(), + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/divider._verticalTextDivider.txt b/tdesign-component/example/assets/code/divider._verticalTextDivider.txt new file mode 100644 index 000000000..6c2308e00 --- /dev/null +++ b/tdesign-component/example/assets/code/divider._verticalTextDivider.txt @@ -0,0 +1,25 @@ + + Widget _verticalTextDivider(BuildContext context) { + return Column( + children: const [ + TDDivider( + text: '文字信息', + alignment: TextAlignment.left, + ), + SizedBox( + height: 20, + ), + TDDivider( + text: '文字信息', + alignment: TextAlignment.center, + ), + SizedBox( + height: 20, + ), + TDDivider( + text: '文字信息', + alignment: TextAlignment.right, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/drawer._buildBaseSimple.txt b/tdesign-component/example/assets/code/drawer._buildBaseSimple.txt new file mode 100644 index 000000000..dc782b14f --- /dev/null +++ b/tdesign-component/example/assets/code/drawer._buildBaseSimple.txt @@ -0,0 +1,23 @@ + +Widget _buildBaseSimple(BuildContext context) { + /// 获取navBar尺寸 + var renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + return TDButton( + text: '基础抽屉', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDDrawer( + context, + visible: true, + drawerTop: renderBox?.size.height, + items: List.generate(30, (index) => TDDrawerItem(title: '菜单${_nums[index]}')).toList(), + onItemClick: (index, item) { + print('drawer item被点击,index:$index,title:${item.title}'); + }, + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/drawer._buildBottomSimple.txt b/tdesign-component/example/assets/code/drawer._buildBottomSimple.txt new file mode 100644 index 000000000..1a95a1790 --- /dev/null +++ b/tdesign-component/example/assets/code/drawer._buildBottomSimple.txt @@ -0,0 +1,28 @@ + +Widget _buildBottomSimple(BuildContext context) { + /// 获取navBar尺寸 + var renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + return TDButton( + text: '带底部插槽样式', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDDrawer( + context, + visible: true, + drawerTop: renderBox?.size.height, + title: '标题', + placement: TDDrawerPlacement.left, + items: List.generate(10, (index) => TDDrawerItem(title: '菜单${_nums[index]}')).toList(), + footer: const TDButton( + text: '操作', + type: TDButtonType.outline, + width: double.infinity, + size: TDButtonSize.large, + ), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/drawer._buildColorSimple.txt b/tdesign-component/example/assets/code/drawer._buildColorSimple.txt new file mode 100644 index 000000000..5b4109820 --- /dev/null +++ b/tdesign-component/example/assets/code/drawer._buildColorSimple.txt @@ -0,0 +1,22 @@ + +Widget _buildColorSimple(BuildContext context) { + var renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + return TDButton( + text: '自定义背景色', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDDrawer( + context, + visible: true, + drawerTop: renderBox?.size.height, + title: '标题', + backgroundColor: TDTheme.of(context).grayColor1, + placement: TDDrawerPlacement.right, + items: List.generate(10, (index) => TDDrawerItem(title: '菜单${_nums[index]}')).toList(), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/drawer._buildIconSimple.txt b/tdesign-component/example/assets/code/drawer._buildIconSimple.txt new file mode 100644 index 000000000..7b444146c --- /dev/null +++ b/tdesign-component/example/assets/code/drawer._buildIconSimple.txt @@ -0,0 +1,20 @@ + +Widget _buildIconSimple(BuildContext context) { + /// 获取navBar尺寸 + var renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + return TDButton( + text: '带图标抽屉', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDDrawer( + context, + visible: true, + drawerTop: renderBox?.size.height, + items: List.generate(30, (index) => TDDrawerItem(title: '菜单${_nums[index]}', icon: const Icon(TDIcons.app))).toList(), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/drawer._buildTitleSimple.txt b/tdesign-component/example/assets/code/drawer._buildTitleSimple.txt new file mode 100644 index 000000000..10d327d99 --- /dev/null +++ b/tdesign-component/example/assets/code/drawer._buildTitleSimple.txt @@ -0,0 +1,22 @@ + +Widget _buildTitleSimple(BuildContext context) { + /// 获取navBar尺寸 + var renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + return TDButton( + text: '带图标抽屉', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDDrawer( + context, + visible: true, + drawerTop: renderBox?.size.height, + title: '标题', + placement: TDDrawerPlacement.left, + items: List.generate(10, (index) => TDDrawerItem(title: '菜单${_nums[index]}')).toList(), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dropdownMenu._buildCustomOverflow.txt b/tdesign-component/example/assets/code/dropdownMenu._buildCustomOverflow.txt new file mode 100644 index 000000000..58b888337 --- /dev/null +++ b/tdesign-component/example/assets/code/dropdownMenu._buildCustomOverflow.txt @@ -0,0 +1,32 @@ + +TDDropdownMenu _buildCustomOverflow(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.up, + onMenuOpened: (value) { + print('打开第$value个菜单'); + }, + onMenuClosed: (value) { + print('关闭第$value个菜单'); + }, + items: [ + TDDropdownItem( + options: [ + TDDropdownItemOption(label: '全部产品', value: 'all', selected: true, selectedColor: Colors.red), + TDDropdownItemOption(label: '最新产品', value: 'new', selectedColor: Colors.blue), + TDDropdownItemOption(label: '最火产品', value: 'hot', selectedColor: Colors.green), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + TDDropdownItem( + multiple: true, + options: [ + TDDropdownItemOption(label: '默认排序', value: 'default', selected: true, selectedColor: Colors.red), + TDDropdownItemOption(label: '价格从高到低', value: 'price', selectedColor: Colors.green), + + ], + ), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dropdownMenu._buildDisabled.txt b/tdesign-component/example/assets/code/dropdownMenu._buildDisabled.txt new file mode 100644 index 000000000..ccf7ae9e9 --- /dev/null +++ b/tdesign-component/example/assets/code/dropdownMenu._buildDisabled.txt @@ -0,0 +1,18 @@ + +TDDropdownMenu _buildDisabled(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.down, + builder: (context) { + return [ + const TDDropdownItem( + disabled: true, + label: '禁用菜单', + ), + const TDDropdownItem( + disabled: true, + label: '禁用菜单', + ), + ]; + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dropdownMenu._buildDownChunk.txt b/tdesign-component/example/assets/code/dropdownMenu._buildDownChunk.txt new file mode 100644 index 000000000..a66fd7918 --- /dev/null +++ b/tdesign-component/example/assets/code/dropdownMenu._buildDownChunk.txt @@ -0,0 +1,86 @@ + +TDDropdownMenu _buildDownChunk(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.down, + items: [ + TDDropdownItem( + label: '单列多选', + multiple: true, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + TDDropdownItemOption(label: '选项3', value: '3'), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '禁用选项', value: '9', disabled: true), + ], + onChange: (value) { + print('选择:$value'); + }, + onConfirm: (value) { + print('确定选择:$value'); + }, + onReset: () { + print('清空选择'); + }, + ), + TDDropdownItem( + // label: '双列单选', + multiple: false, + optionsColumns: 2, + maxHeight: 300, + options: [ + TDDropdownItemOption(label: '双列单选1', value: '1'), + TDDropdownItemOption(label: '双列单选2', value: '2', selected: true), + TDDropdownItemOption(label: '双列单选3', value: '3'), + TDDropdownItemOption(label: '双列单选4', value: '4'), + TDDropdownItemOption(label: '双列单选5', value: '5'), + TDDropdownItemOption(label: '双列单选6', value: '6'), + TDDropdownItemOption(label: '双列单选7', value: '7'), + TDDropdownItemOption(label: '双列单选8', value: '8'), + TDDropdownItemOption(label: '禁用选项', value: '9', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + ], + ), + TDDropdownItem( + label: '双列多选', + multiple: true, + optionsColumns: 2, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2', selected: true), + TDDropdownItemOption(label: '选项3', value: '3'), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '禁用选项', value: '9', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + ], + ), + TDDropdownItem( + label: '三列多选', + multiple: true, + optionsColumns: 3, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2', selected: true), + TDDropdownItemOption(label: '选项3', value: '3', selected: true), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '选项9', value: '9'), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '11', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '12', disabled: true), + ], + ), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dropdownMenu._buildDownSimple.txt b/tdesign-component/example/assets/code/dropdownMenu._buildDownSimple.txt new file mode 100644 index 000000000..6b831a32c --- /dev/null +++ b/tdesign-component/example/assets/code/dropdownMenu._buildDownSimple.txt @@ -0,0 +1,30 @@ + +TDDropdownMenu _buildDownSimple(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.down, + onMenuOpened: (value) { + print('打开第$value个菜单'); + }, + onMenuClosed: (value) { + print('关闭第$value个菜单'); + }, + items: [ + TDDropdownItem( + options: [ + TDDropdownItemOption(label: '全部产品', value: 'all', selected: true), + TDDropdownItemOption(label: '最新产品', value: 'new'), + TDDropdownItemOption(label: '最火产品', value: 'hot'), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + TDDropdownItem( + options: [ + TDDropdownItemOption(label: '默认排序', value: 'default', selected: true), + TDDropdownItemOption(label: '价格从高到低', value: 'price'), + ], + ), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dropdownMenu._buildGroup.txt b/tdesign-component/example/assets/code/dropdownMenu._buildGroup.txt new file mode 100644 index 000000000..aac77ed41 --- /dev/null +++ b/tdesign-component/example/assets/code/dropdownMenu._buildGroup.txt @@ -0,0 +1,32 @@ + +TDDropdownMenu _buildGroup(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.up, + builder: (context) { + return [ + TDDropdownItem( + label: '分组菜单', + multiple: true, + optionsColumns: 3, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true, group: '类型'), + TDDropdownItemOption(label: '选项2', value: '2', group: '类型'), + TDDropdownItemOption(label: '选项3', value: '3', group: '类型'), + TDDropdownItemOption(label: '选项4', value: '4', group: '类型'), + TDDropdownItemOption(label: '选项5', value: '5', group: '角色'), + TDDropdownItemOption(label: '选项6', value: '6', group: '角色'), + TDDropdownItemOption(label: '选项7', value: '7', group: '角色'), + TDDropdownItemOption(label: '选项8', value: '8', group: '角色'), + TDDropdownItemOption(label: '禁用选项', value: '9', disabled: true, group: '角色'), + ], + onChange: (value) { + print('选择:$value'); + }, + onConfirm: (value) { + print('确定选择:$value'); + }, + ), + ]; + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dropdownMenu._buildHeight.txt b/tdesign-component/example/assets/code/dropdownMenu._buildHeight.txt new file mode 100644 index 000000000..24687f08a --- /dev/null +++ b/tdesign-component/example/assets/code/dropdownMenu._buildHeight.txt @@ -0,0 +1,55 @@ + +TDDropdownMenu _buildHeight(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.up, + onMenuOpened: (value) { + print('打开第$value个菜单'); + }, + onMenuClosed: (value) { + print('关闭第$value个菜单'); + }, + builder: (context) { + return [ + TDDropdownItem( + label: '最大高度限制', + multiple: true, + maxHeight: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2', selected: true), + TDDropdownItemOption(label: '选项3', value: '3', selected: true), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '选项9', value: '9'), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '11', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '12', disabled: true), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + TDDropdownItem( + maxHeight: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + TDDropdownItemOption(label: '选项3', value: '3'), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '选项9', value: '9'), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '11', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '12', disabled: true), + ], + ), + ]; + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dropdownMenu._buildHidden.txt b/tdesign-component/example/assets/code/dropdownMenu._buildHidden.txt new file mode 100644 index 000000000..2b7b08cca --- /dev/null +++ b/tdesign-component/example/assets/code/dropdownMenu._buildHidden.txt @@ -0,0 +1,30 @@ + +TDDropdownMenu _buildHidden(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.auto, + arrowIcon: TDIcons.caret_up_small, + builder: (context) { + return [ + TDDropdownItem( + label: '分组菜单', + multiple: true, + optionsColumns: 3, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true, group: '类型'), + TDDropdownItemOption(label: '选项2', value: '2', group: '类型'), + TDDropdownItemOption(label: '选项3', value: '3', group: '类型'), + TDDropdownItemOption(label: '选项4', value: '4', group: '类型'), + TDDropdownItemOption(label: '选项5', value: '5', group: '角色'), + TDDropdownItemOption(label: '选项6', value: '6', group: '角色'), + TDDropdownItemOption(label: '选项7', value: '7', group: '角色'), + TDDropdownItemOption(label: '选项8', value: '8', group: '角色'), + TDDropdownItemOption(label: '禁用选项', value: '9', disabled: true, group: '角色'), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + ]; + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dropdownMenu._buildOverflow.txt b/tdesign-component/example/assets/code/dropdownMenu._buildOverflow.txt new file mode 100644 index 000000000..4e8806d4e --- /dev/null +++ b/tdesign-component/example/assets/code/dropdownMenu._buildOverflow.txt @@ -0,0 +1,71 @@ + +TDDropdownMenu _buildOverflow(BuildContext context) { + return TDDropdownMenu( + isScrollable: true, + tabBarAlign: MainAxisAlignment.spaceAround, + direction: TDDropdownMenuDirection.up, + onMenuOpened: (value) { + print('打开第$value个菜单'); + }, + onMenuClosed: (value) { + print('关闭第$value个菜单'); + }, + builder: (context) { + return [ + TDDropdownItem( + label: '最大高度限制', + multiple: true, + maxHeight: 200, + tabBarWidth: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2', selected: true), + TDDropdownItemOption(label: '选项3', value: '3', selected: true), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '选项9', value: '9'), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '11', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '12', disabled: true), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + TDDropdownItem( + maxHeight: 200, + tabBarWidth: 200, + tabBarAlign: MainAxisAlignment.start, + options: [ + TDDropdownItemOption(label: '选项1选项1选项1选项1选项1选项1选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + ], + ), + TDDropdownItem( + maxHeight: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + ], + ), + TDDropdownItem( + maxHeight: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + ], + ), + TDDropdownItem( + maxHeight: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + ], + ), + ]; + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/dropdownMenu._buildUp.txt b/tdesign-component/example/assets/code/dropdownMenu._buildUp.txt new file mode 100644 index 000000000..412ae0101 --- /dev/null +++ b/tdesign-component/example/assets/code/dropdownMenu._buildUp.txt @@ -0,0 +1,32 @@ + +TDDropdownMenu _buildUp(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.up, + onMenuOpened: (value) { + print('打开第$value个菜单'); + }, + onMenuClosed: (value) { + print('关闭第$value个菜单'); + }, + builder: (context) { + return [ + TDDropdownItem( + options: [ + TDDropdownItemOption(label: '全部产品', value: 'all', selected: true), + TDDropdownItemOption(label: '最新产品', value: 'new'), + TDDropdownItemOption(label: '最火产品', value: 'hot'), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + TDDropdownItem( + options: [ + TDDropdownItemOption(label: '默认排序', value: 'default', selected: true), + TDDropdownItemOption(label: '价格从高到低', value: 'price'), + ], + ), + ]; + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/empty._iconEmpty.txt b/tdesign-component/example/assets/code/empty._iconEmpty.txt new file mode 100644 index 000000000..123d960e6 --- /dev/null +++ b/tdesign-component/example/assets/code/empty._iconEmpty.txt @@ -0,0 +1,7 @@ + + Widget _iconEmpty(BuildContext context) { + return const TDEmpty( + type: TDEmptyType.plain, + emptyText: '描述文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/empty._imageEmpty.txt b/tdesign-component/example/assets/code/empty._imageEmpty.txt new file mode 100644 index 000000000..9600b28c2 --- /dev/null +++ b/tdesign-component/example/assets/code/empty._imageEmpty.txt @@ -0,0 +1,14 @@ + + Widget _imageEmpty(BuildContext context) { + return TDEmpty( + type: TDEmptyType.plain, + emptyText: '描述文字', + image: Container( + width: 120, + height: 120, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault), + image: const DecorationImage(image: AssetImage('assets/img/empty.png'))), + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/empty._operationCustomEmpty.txt b/tdesign-component/example/assets/code/empty._operationCustomEmpty.txt new file mode 100644 index 000000000..7ac51d368 --- /dev/null +++ b/tdesign-component/example/assets/code/empty._operationCustomEmpty.txt @@ -0,0 +1,16 @@ + + Widget _operationCustomEmpty(BuildContext context) { + return TDEmpty( + type: TDEmptyType.operation, + emptyText: '描述文字', + customOperationWidget: Padding( + padding: const EdgeInsets.only(top: 32), + child: TDButton( + text: '自定义操作按钮', + size: TDButtonSize.medium, + theme: TDButtonTheme.danger, + width: 160, + onTap: () {}, + )), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/empty._operationEmpty.txt b/tdesign-component/example/assets/code/empty._operationEmpty.txt new file mode 100644 index 000000000..9293c5447 --- /dev/null +++ b/tdesign-component/example/assets/code/empty._operationEmpty.txt @@ -0,0 +1,8 @@ + + Widget _operationEmpty(BuildContext context) { + return const TDEmpty( + type: TDEmptyType.operation, + operationText: '操作按钮', + emptyText: '描述文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/fab._buildPureIconFab.txt b/tdesign-component/example/assets/code/fab._buildPureIconFab.txt new file mode 100644 index 000000000..131684ecd --- /dev/null +++ b/tdesign-component/example/assets/code/fab._buildPureIconFab.txt @@ -0,0 +1,8 @@ + + Widget _buildPureIconFab(BuildContext context) { + return _buildRowDemo([ + const TDFab( + theme: TDFabTheme.primary, + ) + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/fab._buildShapeFab.txt b/tdesign-component/example/assets/code/fab._buildShapeFab.txt new file mode 100644 index 000000000..65c00e0cf --- /dev/null +++ b/tdesign-component/example/assets/code/fab._buildShapeFab.txt @@ -0,0 +1,19 @@ + + Widget _buildShapeFab(BuildContext context) { + return _buildRowDemoWidthDescription([ + { + 'component': const TDFab( + theme: TDFabTheme.primary, + shape: TDFabShape.circle, + ), + 'desc': 'Circle' + }, + { + 'component': const TDFab( + theme: TDFabTheme.primary, + shape: TDFabShape.square, + ), + 'desc': 'Square' + }, + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/fab._buildSizeFab.txt b/tdesign-component/example/assets/code/fab._buildSizeFab.txt new file mode 100644 index 000000000..6936654aa --- /dev/null +++ b/tdesign-component/example/assets/code/fab._buildSizeFab.txt @@ -0,0 +1,33 @@ + + Widget _buildSizeFab(BuildContext context) { + return _buildRowDemoWidthDescription([ + { + 'component': const TDFab( + theme: TDFabTheme.primary, + size: TDFabSize.large, + ), + 'desc': 'Large' + }, + { + 'component': const TDFab( + theme: TDFabTheme.primary, + size: TDFabSize.medium, + ), + 'desc': 'Medium' + }, + { + 'component': const TDFab( + theme: TDFabTheme.primary, + size: TDFabSize.small, + ), + 'desc': 'Small' + }, + { + 'component': const TDFab( + theme: TDFabTheme.primary, + size: TDFabSize.extraSmall, + ), + 'desc': 'extraSmall' + }, + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/fab._buildTextFab.txt b/tdesign-component/example/assets/code/fab._buildTextFab.txt new file mode 100644 index 000000000..6aa7e1acf --- /dev/null +++ b/tdesign-component/example/assets/code/fab._buildTextFab.txt @@ -0,0 +1,9 @@ + + Widget _buildTextFab(BuildContext context) { + return _buildRowDemo([ + const TDFab( + theme: TDFabTheme.primary, + text: 'Floating', + ) + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/fab._buildThemeFab.txt b/tdesign-component/example/assets/code/fab._buildThemeFab.txt new file mode 100644 index 000000000..37b41baaa --- /dev/null +++ b/tdesign-component/example/assets/code/fab._buildThemeFab.txt @@ -0,0 +1,29 @@ + + Widget _buildThemeFab(BuildContext context) { + return _buildRowDemoWidthDescription([ + { + 'component': const TDFab( + theme: TDFabTheme.primary, + ), + 'desc': 'Primary' + }, + { + 'component': const TDFab( + theme: TDFabTheme.defaultTheme, + ), + 'desc': 'Default' + }, + { + 'component': const TDFab( + theme: TDFabTheme.light, + ), + 'desc': 'Light' + }, + { + 'component': const TDFab( + theme: TDFabTheme.danger, + ), + 'desc': 'Danger' + }, + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/footer._buildBrandFooter.txt b/tdesign-component/example/assets/code/footer._buildBrandFooter.txt new file mode 100644 index 000000000..225ac72cb --- /dev/null +++ b/tdesign-component/example/assets/code/footer._buildBrandFooter.txt @@ -0,0 +1,9 @@ + + Widget _buildBrandFooter(BuildContext context) { + return TDFooter( + TDFooterType.brand, + logo: 'assets/img/td_brand.png', + width: 204, + height: 48, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/footer._buildFooter.txt b/tdesign-component/example/assets/code/footer._buildFooter.txt new file mode 100644 index 000000000..feabbacf0 --- /dev/null +++ b/tdesign-component/example/assets/code/footer._buildFooter.txt @@ -0,0 +1,7 @@ + + Widget _buildFooter(BuildContext context) { + return const TDFooter( + TDFooterType.text, + text: 'Copyright © 2019-2023 TDesign.All Rights Reserved.', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/footer._buildLinksFooter.txt b/tdesign-component/example/assets/code/footer._buildLinksFooter.txt new file mode 100644 index 000000000..fc4709911 --- /dev/null +++ b/tdesign-component/example/assets/code/footer._buildLinksFooter.txt @@ -0,0 +1,31 @@ + + Widget _buildLinksFooter(BuildContext context) { + final links = [ + TDLink( + label: '底部链接1', + style: TDLinkStyle.primary, + uri: Uri.parse('https://example.com'), + linkClick: (link) { + print('点击了链接1 $link'); + }, + ), + TDLink( + label: '底部链接2', + style: TDLinkStyle.primary, + uri: Uri.parse('https://example.com'), + linkClick: (link) { + print('点击了链接2 $link'); + }, + ), + ]; + return Column( + children: [ + const SizedBox(height: 12), + TDFooter( + TDFooterType.link, + links: links, + text: 'Copyright © 2019-2023 TDesign.All Rights Reserved.', + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/footer._buildSingleLinkFooter.txt b/tdesign-component/example/assets/code/footer._buildSingleLinkFooter.txt new file mode 100644 index 000000000..656406812 --- /dev/null +++ b/tdesign-component/example/assets/code/footer._buildSingleLinkFooter.txt @@ -0,0 +1,21 @@ + + Widget _buildSingleLinkFooter(BuildContext context) { + // 示例链接列表 + final singleLink = [ + TDLink( + label: '底部链接', + style: TDLinkStyle.primary, + // type: TDLinkType.withSuffixIcon, + uri: Uri.parse('https://example.com'), + linkClick: (link) { + print('点击了链接 $link'); + }, + ), + ]; + + return TDFooter( + TDFooterType.link, + links: singleLink, + text: 'Copyright © 2019-2023 TDesign.All Rights Reserved.', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/form._buildArrangementSwitch.txt b/tdesign-component/example/assets/code/form._buildArrangementSwitch.txt new file mode 100644 index 000000000..e9899aa7a --- /dev/null +++ b/tdesign-component/example/assets/code/form._buildArrangementSwitch.txt @@ -0,0 +1,80 @@ + + Widget _buildArrangementSwitch(BuildContext buildContext) { + final theme = TDTheme.of(context); + return Container( + decoration: BoxDecoration( + color: theme.whiteColor1, + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Expanded( + child: TDButton( + text: '水平排布', + shape: TDButtonShape.round, + style: TDButtonStyle(backgroundColor: horizontalButtonColor), + textStyle: TextStyle( + fontWeight: FontWeight.w700, + color: horizontalTextColor, + ), + onTap: () { + setState(() { + if (horizontalButton) { + /// 置换按钮状态 + horizontalButton = false; + verticalButton = true; + + /// 置换按钮颜色 + final currentVerticalColor = verticalButtonColor; + verticalButtonColor = horizontalButtonColor; + horizontalButtonColor = currentVerticalColor; + + /// 置换文字颜色 + final currentTextColor = verticalTextColor; + verticalTextColor = horizontalTextColor; + horizontalTextColor = currentTextColor; + _isFormHorizontal = true; + print(_isFormHorizontal); + } + }); + }, + ), + ), + const SizedBox(width: 8), + Expanded( + child: TDButton( + text: '竖直排布', + shape: TDButtonShape.round, + style: TDButtonStyle(backgroundColor: verticalButtonColor), + textStyle: TextStyle( + fontWeight: FontWeight.w700, + color: verticalTextColor, + ), + onTap: () { + setState(() { + if (verticalButton) { + /// 置换按钮状态 + horizontalButton = true; + verticalButton = false; + + /// 置换按钮颜色 + final currentVerticalColor = verticalButtonColor; + verticalButtonColor = horizontalButtonColor; + horizontalButtonColor = currentVerticalColor; + + /// 置换文字颜色 + final currentTextColor = verticalTextColor; + verticalTextColor = horizontalTextColor; + horizontalTextColor = currentTextColor; + + _isFormHorizontal = false; + print(_isFormHorizontal); + } + }); + }, + ), + ), + ], + ))); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/form._buildForm.txt b/tdesign-component/example/assets/code/form._buildForm.txt new file mode 100644 index 000000000..845802acc --- /dev/null +++ b/tdesign-component/example/assets/code/form._buildForm.txt @@ -0,0 +1,327 @@ + + Widget _buildForm(BuildContext context) { + final theme = TDTheme.of(context); + return TDForm( + formController: _formController, + disabled: _formDisableState, + data: _formData, + isHorizontal: _isFormHorizontal, + rules: _validationRules, + formContentAlign: TextAlign.left, + requiredMark: true, + + /// 确定整个表单是否展示提示信息 + formShowErrorMessage: true, + onSubmit: onSubmit, + items: [ + TDFormItem( + label: '用户名', + name: 'name', + type: TDFormItemType.input, + help: '请输入用户名', + labelWidth: 82.0, + formItemNotifier: _formItemNotifier['name'], + + /// 控制单个 item 是否展示错误提醒 + showErrorMessage: true, + requiredMark: true, + child: TDInput( + leftContentSpace: 0, + inputDecoration: InputDecoration( + hintText: "请输入用户名", + border: InputBorder.none, + contentPadding: EdgeInsets.all(0), + hintStyle: TextStyle(color: TDTheme.of(context).fontGyColor3.withOpacity(0.4))), + controller: _controller[0], + backgroundColor: Colors.white, + additionInfoColor: TDTheme.of(context).errorColor6, + showBottomDivider: false, + readOnly: _formDisableState, + onChanged: (val) { + _formItemNotifier['name']?.upDataForm(val); + }, + onClearTap: () { + _controller[0].clear(); + _formItemNotifier['name']?.upDataForm(""); + }), + ), + TDFormItem( + label: '密码', + name: 'password', + type: TDFormItemType.input, + labelWidth: 82.0, + formItemNotifier: _formItemNotifier['password'], + showErrorMessage: true, + child: TDInput( + leftContentSpace: 0, + inputDecoration: InputDecoration( + hintText: '请输入密码', + border: InputBorder.none, + hintStyle: TextStyle(color: TDTheme.of(context).fontGyColor3.withOpacity(0.4))), + type: TDInputType.normal, + controller: _controller[1], + obscureText: !browseOn, + backgroundColor: Colors.white, + needClear: false, + readOnly: _formDisableState, + showBottomDivider: false, + onChanged: (val) { + _formItemNotifier['password']?.upDataForm(val); + }, + onClearTap: () { + _controller[1].clear(); + _formItemNotifier['password']?.upDataForm(""); + }), + ), + TDFormItem( + label: '性别', + name: 'gender', + type: TDFormItemType.radios, + labelWidth: 82.0, + showErrorMessage: true, + formItemNotifier: _formItemNotifier['gender'], + child: TDRadioGroup( + spacing: 0, + direction: Axis.horizontal, + controller: _genderCheckboxGroupController, + directionalTdRadios: _radios.entries.map((entry) { + return TDRadio( + id: entry.key, + title: entry.value, + radioStyle: TDRadioStyle.circle, + showDivider: false, + spacing: 4, + checkBoxLeftSpace: 0, + customSpace: EdgeInsets.all(0), + enable: !_formDisableState, + ); + }).toList(), + onRadioGroupChange: (ids) { + if (ids == null) { + return; + } + _formItemNotifier['gender']?.upDataForm(ids); + }, + ), + ), + TDFormItem( + label: '生日', + name: 'birth', + labelWidth: 82.0, + type: TDFormItemType.dateTimePicker, + contentAlign: TextAlign.left, + tipAlign: TextAlign.left, + formItemNotifier: _formItemNotifier['birth'], + hintText:'请输入内容', + select: _selected_1, + selectFn: (BuildContext context) { + if (_formDisableState) { + return; + } + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + _selected_1 = + '${selected['year'].toString().padLeft(4, '0')}-${selected['month'].toString().padLeft(2, '0')}-${selected['day'].toString().padLeft(2, '0')}'; + _formItemNotifier['birth']?.upDataForm(_selected_1); + }); + Navigator.of(context).pop(); + }, dateStart: [1999, 01, 01], dateEnd: [2050, 12, 31], initialDate: [2012, 1, 1]); + }, + ), + TDFormItem( + label: '籍贯', + name: 'place', + type: TDFormItemType.cascader, + contentAlign: TextAlign.left, + tipAlign: TextAlign.left, + labelWidth: 82.0, + hintText:'请输入内容', + select: _selected_2, + formItemNotifier: _formItemNotifier['place'], + selectFn: (BuildContext context) { + if (_formDisableState) { + return; + } + TDCascader.showMultiCascader(context, + title: '选择地址', + data: _data, + initialData: _initLocalData, + theme: 'step', onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initLocalData = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_2 = result.join('/'); + _formItemNotifier['place']?.upDataForm(_selected_2); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + ), + TDFormItem( + label: '年限', + name: 'age', + labelWidth: 82.0, + type: TDFormItemType.stepper, + formItemNotifier: _formItemNotifier['age'], + child: Padding( + padding: EdgeInsets.only( right: 18), + child: TDStepper( + theme: TDStepperTheme.filled, + disabled: _formDisableState, + eventController: _stepController!, + value:int.parse(_formData['age']), + onChange: (value) { + _formItemNotifier['age']?.upDataForm('${value}'); + }, + ), + )), + TDFormItem( + label: '自我评价', + name: 'description', + tipAlign: TextAlign.left, + type: TDFormItemType.rate, + labelWidth: 82.0, + formItemNotifier: _formItemNotifier['description'], + child: Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: EdgeInsets.only(right: 18), + child: TDRate( + count: 5, + value: double.parse(_formData['description']), + allowHalf: false, + disabled: _formDisableState, + onChange: (value) { + setState(() { + _formData['description'] = '${value}'; + }); + _formItemNotifier['description']?.upDataForm('${value}'); + }, + )), + ), + ), + TDFormItem( + label: '个人简介', + labelWidth: 82.0, + name: 'resume', + type: TDFormItemType.textarea, + formItemNotifier: _formItemNotifier['resume'], + child: Padding( + padding: EdgeInsets.only(top: _isFormHorizontal?0:8,bottom: 4), + child: TDTextarea( + backgroundColor: Colors.red, + padding: EdgeInsets.all(0), + hintText: '请输入个人简介', + maxLength: 500, + indicator: true, + readOnly: _formDisableState, + layout: TDTextareaLayout.vertical, + controller: _controller[2], + showBottomDivider: false, + onChanged: (value) { + _formItemNotifier['resume']?.upDataForm(value); + }, + ), + )), + TDFormItem( + label: '上传图片', + name: 'photo', + labelWidth: 82.0, + type: TDFormItemType.upLoadImg, + formItemNotifier: _formItemNotifier['photo'], + child: Padding( + padding: EdgeInsets.only(top:4,bottom: 4), + child: TDUpload( + files: files, + multiple: true, + max: 6, + onError: print, + onValidate: print, + disabled: _formDisableState, + onChange: ((imgList, type) { + if (_formDisableState) { + return; + } + files = _onValueChanged(files ?? [], imgList, type); + List imgs = files.map((e) => e.remotePath ?? e.assetPath).toList(); + setState(() { + _formItemNotifier['photo'].upDataForm(imgs.join(',')); + }); + }), + ), + )) + ], + btnGroup: [ + Container( + decoration: BoxDecoration( + color: theme.whiteColor1, + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Expanded( + child: TDButton( + text: '重置', + size: TDButtonSize.large, + type: TDButtonType.fill, + theme: TDButtonTheme.light, + shape: TDButtonShape.rectangle, + disabled: _formDisableState, + onTap: () { + //用户名称 + _controller[0].clear(); + //密码 + _controller[1].clear(); + // 性别 + _genderCheckboxGroupController.toggle('', false); + //个人简介 + _controller[2].clear(); + //生日 + _selected_1 = ''; + //籍贯 + _selected_2 = ''; + //年限 + _stepController.add(TDStepperEventType.cleanValue); + //上传图片 + files.clear(); + _formData = { + "name": '', + "password": '', + "gender": '', + "birth": '', + "place": '', + "age": "0", + "description": "2", + "resume": '', + "photo": '' + }; + _formData.forEach((key, value) { + _formItemNotifier[key].upDataForm(value); + }); + _formController.reset(_formData); + setState(() {}); + }, + )), + SizedBox( + width: 20, + ), + Expanded( + child: TDButton( + text: '提交', + size: TDButtonSize.large, + type: TDButtonType.fill, + theme: TDButtonTheme.primary, + shape: TDButtonShape.rectangle, + onTap: _onSubmit, + disabled: _formDisableState)), + ], + )), + ) + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/icon._showAllIcons.txt b/tdesign-component/example/assets/code/icon._showAllIcons.txt new file mode 100644 index 000000000..26d2727b5 --- /dev/null +++ b/tdesign-component/example/assets/code/icon._showAllIcons.txt @@ -0,0 +1,113 @@ + + Widget _showAllIcons(BuildContext context) { + return Container( + color: Colors.white, + alignment: Alignment.center, + child: Column( + children: [ + Container( + padding: const EdgeInsets.only(left: 16), + alignment: Alignment.topLeft, + child: const Wrap( + children: [ + TDText('筛选Icon请前往TDesign官网(长按网址可复制):'), + SelectableText('https://tdesign.tencent.com/vue/components/icon') + ], + ), + ), + TDSearchBar( + action: '搜索', + onActionClick: (text) { + setState(() { + iconList = []; + isLoading = true; + }); + Future.delayed(const Duration(milliseconds: 30), () { + var list = []; + TDIcons.all.forEach((key, value) { + if (value.name.contains(text)) { + list.add(value); + } + }); + setState(() { + iconList = list; + isLoading = false; + }); + }); + }, + onClearClick: (_) { + setState(() { + iconList = TDIcons.all.values; + }); + }, + ), + Container( + child: TDButton( + text: showBorder ? '隐藏边框' : '显示边框', + shape: TDButtonShape.filled, + onTap: () { + setState(() { + showBorder = !showBorder; + }); + }, + ), + margin: const EdgeInsets.only(bottom: 16), + ), + Builder(builder: (context) { + if (iconList.isEmpty) { + return Container( + height: 300, + alignment: Alignment.center, + child: isLoading ? const TDText('加载中...') : const TDText('暂无内容'), + ); + } + return SizedBox( + height: MediaQuery.of(context).size.height - 150, + child: ListView.builder( + itemCount: (iconList.length + 1) ~/ 2, + itemBuilder: (context,index){ + var index1 = index ~/ 2; + var index2 = index1 + 1; + var iconData1 = iconList.elementAt(index1); + var iconData2; + if(iconList.length > index2){ + iconData2 = iconList.elementAt(index2); + } + return Row( + children: [ + SizedBox( + height: 100, + width: 175, + child: Column( + children: [ + Container( + color: showBorder ? TDTheme.of(context).brandDisabledColor : Colors.transparent, + child: Icon(iconData1), + ), + TDText(iconData1.name) + ], + ), + ), + if (iconData2 != null) + SizedBox( + height: 100, + width: 175, + child: Column( + children: [ + Container( + color: showBorder ? TDTheme.of(context).brandDisabledColor : Colors.transparent, + child: Icon(iconData2), + ), + TDText(iconData2.name) + ], + ), + ) + ], + ); + }), + ); + }) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._fail.txt b/tdesign-component/example/assets/code/image._fail.txt new file mode 100644 index 000000000..fb71b4c17 --- /dev/null +++ b/tdesign-component/example/assets/code/image._fail.txt @@ -0,0 +1,60 @@ + + Widget _fail(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 24), + child: Row( + children: [ + const SizedBox( + width: 16, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '失败默认提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + const TDImage( + imgUrl: 'error', + type: TDImageType.roundedSquare, + ), + ], + ), + const SizedBox( + width: 24, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '失败自定义提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + TDImage( + imgUrl: 'error', + errorWidget: TDText( + '加载失败', + forceVerticalCenter: true, + font: TDTheme.of(context).fontBodyExtraSmall, + fontWeight: FontWeight.w500, + textColor: TDTheme.of(context).fontGyColor3, + ), + type: TDImageType.roundedSquare, + ), + ], + ), + const SizedBox( + width: 24, + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._failCustom.txt b/tdesign-component/example/assets/code/image._failCustom.txt new file mode 100644 index 000000000..13d98fdfb --- /dev/null +++ b/tdesign-component/example/assets/code/image._failCustom.txt @@ -0,0 +1,27 @@ + + Widget _failCustom(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '失败自定义提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + TDImage( + imgUrl: 'error', + errorWidget: TDText( + '加载失败', + forceVerticalCenter: true, + font: TDTheme.of(context).fontBodyExtraSmall, + fontWeight: FontWeight.w500, + textColor: TDTheme.of(context).fontGyColor3, + ), + type: TDImageType.roundedSquare, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._failDefault.txt b/tdesign-component/example/assets/code/image._failDefault.txt new file mode 100644 index 000000000..2016c9931 --- /dev/null +++ b/tdesign-component/example/assets/code/image._failDefault.txt @@ -0,0 +1,20 @@ + + Widget _failDefault(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '失败默认提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + const TDImage( + imgUrl: 'error', + type: TDImageType.roundedSquare, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._imageCircle.txt b/tdesign-component/example/assets/code/image._imageCircle.txt new file mode 100644 index 000000000..87220e95f --- /dev/null +++ b/tdesign-component/example/assets/code/image._imageCircle.txt @@ -0,0 +1,22 @@ + + Widget _imageCircle(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '圆形', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + const TDImage( + assetUrl: 'assets/img/image.png', + width: 72, + height: 72, + type: TDImageType.circle, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._imageClip.txt b/tdesign-component/example/assets/code/image._imageClip.txt new file mode 100644 index 000000000..64f33a0af --- /dev/null +++ b/tdesign-component/example/assets/code/image._imageClip.txt @@ -0,0 +1,20 @@ + + Widget _imageClip(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '裁剪', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + const TDImage( + assetUrl: 'assets/img/image.png', + type: TDImageType.clip, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._imageFile.txt b/tdesign-component/example/assets/code/image._imageFile.txt new file mode 100644 index 000000000..a8653919c --- /dev/null +++ b/tdesign-component/example/assets/code/image._imageFile.txt @@ -0,0 +1,11 @@ + + Widget _imageFile(BuildContext context) { + return Container( + width: 72, + height: 72, + child: TDImage( + imageFile: File('/sdcard/td/test.jpg'), + type: TDImageType.fitWidth, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._imageFitHeight.txt b/tdesign-component/example/assets/code/image._imageFitHeight.txt new file mode 100644 index 000000000..9ec5a62ae --- /dev/null +++ b/tdesign-component/example/assets/code/image._imageFitHeight.txt @@ -0,0 +1,25 @@ + + Widget _imageFitHeight(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '适应高', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + width: 89, + height: 72, + color: Colors.black, + child: const TDImage( + assetUrl: 'assets/img/image.png', + type: TDImageType.fitHeight, + ), + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._imageFitWidth.txt b/tdesign-component/example/assets/code/image._imageFitWidth.txt new file mode 100644 index 000000000..a356ad82f --- /dev/null +++ b/tdesign-component/example/assets/code/image._imageFitWidth.txt @@ -0,0 +1,25 @@ + + Widget _imageFitWidth(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '适应宽', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + width: 72, + height: 89, + color: Colors.black, + child: const TDImage( + assetUrl: 'assets/img/image.png', + type: TDImageType.fitWidth, + ), + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._imageRoundedSquare.txt b/tdesign-component/example/assets/code/image._imageRoundedSquare.txt new file mode 100644 index 000000000..58f57d608 --- /dev/null +++ b/tdesign-component/example/assets/code/image._imageRoundedSquare.txt @@ -0,0 +1,22 @@ + + Widget _imageRoundedSquare(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '圆角方形', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + const TDImage( + assetUrl: 'assets/img/image.png', + type: TDImageType.roundedSquare, + width: 72, + height: 72, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._imageSquare.txt b/tdesign-component/example/assets/code/image._imageSquare.txt new file mode 100644 index 000000000..ce6df2091 --- /dev/null +++ b/tdesign-component/example/assets/code/image._imageSquare.txt @@ -0,0 +1,20 @@ + + Widget _imageSquare(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '方形', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + const TDImage( + assetUrl: 'assets/img/image.png', + type: TDImageType.square, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._imageStretch.txt b/tdesign-component/example/assets/code/image._imageStretch.txt new file mode 100644 index 000000000..7343e1b5c --- /dev/null +++ b/tdesign-component/example/assets/code/image._imageStretch.txt @@ -0,0 +1,32 @@ + + Widget _imageStretch(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '拉伸', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + color: Colors.black, + width: 121, + height: 72, + child: Stack( + alignment: Alignment.center, + children: [ + TDImage( + assetUrl: 'assets/img/image.png', + width: 121, + height: 50, + type: TDImageType.stretch, + ), + ], + ), + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._loading.txt b/tdesign-component/example/assets/code/image._loading.txt new file mode 100644 index 000000000..ff4f51ec2 --- /dev/null +++ b/tdesign-component/example/assets/code/image._loading.txt @@ -0,0 +1,95 @@ + + Widget _loading(BuildContext context) { + return Row( + children: [ + const SizedBox( + width: 16, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '加载默认提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + height: 72, + width: 72, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault)), + child: Container( + alignment: Alignment.center, + color: TDTheme.of(context).grayColor2, + child: Icon( + TDIcons.ellipsis, + size: 22, + color: TDTheme.of(context).fontGyColor3, + ) + ) + ), + // 实际组件写法如下:上面仅为加载展示 + // const TDImage( + // imgUrl: + // 'https://images.pexels.com/photos/842711/pexels-photo-842711.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + // type: TDImageType.roundedSquare, + // ), + ], + ), + const SizedBox( + width: 24, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '加载自定义提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + height: 72, + width: 72, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault)), + child: Container( + alignment: Alignment.center, + color: TDTheme.of(context).grayColor2, + child: RotationTransition( + turns: animation, + alignment: Alignment.center, + child: TDCircleIndicator( + color: TDTheme.of(context).brandNormalColor, + size: 18, + lineWidth: 3, + )) + ) + ), + // 实际组件写法如下:上面仅为加载展示 + // TDImage( + // imgUrl: + // 'https://images.pexels.com/photos/842711/pexels-photo-842711.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + // loadingWidget: RotationTransition( + // turns: animation, + // alignment: Alignment.center, + // child: TDCircleIndicator( + // color: TDTheme.of(context).brandNormalColor, + // size: 18, + // lineWidth: 3, + // )), + // type: TDImageType.roundedSquare, + // ), + ], + ), + const SizedBox( + width: 24, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._loadingCustom.txt b/tdesign-component/example/assets/code/image._loadingCustom.txt new file mode 100644 index 000000000..a3542f7ef --- /dev/null +++ b/tdesign-component/example/assets/code/image._loadingCustom.txt @@ -0,0 +1,48 @@ + + Widget _loadingCustom(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '加载自定义提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + height: 72, + width: 72, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + child: Container( + alignment: Alignment.center, + color: TDTheme.of(context).grayColor2, + child: RotationTransition( + turns: animation, + alignment: Alignment.center, + child: TDCircleIndicator( + color: TDTheme.of(context).brandNormalColor, + size: 18, + lineWidth: 3, + )))), + // 实际组件写法如下:上面仅为加载展示 + // TDImage( + // imgUrl: + // 'https://images.pexels.com/photos/842711/pexels-photo-842711.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + // loadingWidget: RotationTransition( + // turns: animation, + // alignment: Alignment.center, + // child: TDCircleIndicator( + // color: TDTheme.of(context).brandNormalColor, + // size: 18, + // lineWidth: 3, + // )), + // type: TDImageType.roundedSquare, + // ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image._loadingDefault.txt b/tdesign-component/example/assets/code/image._loadingDefault.txt new file mode 100644 index 000000000..edd83e589 --- /dev/null +++ b/tdesign-component/example/assets/code/image._loadingDefault.txt @@ -0,0 +1,37 @@ + + Widget _loadingDefault(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '加载默认提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + height: 72, + width: 72, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + child: Container( + alignment: Alignment.center, + color: TDTheme.of(context).grayColor2, + child: Icon( + TDIcons.ellipsis, + size: 22, + color: TDTheme.of(context).fontGyColor3, + ))), + // 实际组件写法如下:上面仅为加载展示 + // const TDImage( + // imgUrl: + // 'https://images.pexels.com/photos/842711/pexels-photo-842711.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + // type: TDImageType.roundedSquare, + // ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image_viewer._actionImageViewer.txt b/tdesign-component/example/assets/code/image_viewer._actionImageViewer.txt new file mode 100644 index 000000000..d513b8d7b --- /dev/null +++ b/tdesign-component/example/assets/code/image_viewer._actionImageViewer.txt @@ -0,0 +1,22 @@ + + Widget _actionImageViewer(BuildContext context) { + var delImages = [ + 'https://tdesign.gtimg.com/mobile/demos/swiper1.png', + 'https://tdesign.gtimg.com/mobile/demos/swiper2.png', + ]; + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '带操作图片预览', + onTap: () { + TDImageViewer.showImageViewer( + context: context, + images: delImages, + showIndex: true, + deleteBtn: true, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image_viewer._basicImageViewer.txt b/tdesign-component/example/assets/code/image_viewer._basicImageViewer.txt new file mode 100644 index 000000000..be9a376f4 --- /dev/null +++ b/tdesign-component/example/assets/code/image_viewer._basicImageViewer.txt @@ -0,0 +1,13 @@ + + Widget _basicImageViewer(BuildContext context) { + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '基础图片预览', + onTap: () { + TDImageViewer.showImageViewer(context: context, images: images); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image_viewer._descImageViewer.txt b/tdesign-component/example/assets/code/image_viewer._descImageViewer.txt new file mode 100644 index 000000000..1e150d523 --- /dev/null +++ b/tdesign-component/example/assets/code/image_viewer._descImageViewer.txt @@ -0,0 +1,22 @@ + + Widget _descImageViewer(BuildContext context) { + var delImages = [ + 'https://tdesign.gtimg.com/mobile/demos/swiper1.png', + 'https://tdesign.gtimg.com/mobile/demos/swiper2.png', + ]; + var labels = ['图片标题1', '图片标题2']; + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '带图片标题', + onTap: () { + TDImageViewer.showImageViewer( + context: context, + images: delImages, + labels: labels, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image_viewer._longPressImageViewer.txt b/tdesign-component/example/assets/code/image_viewer._longPressImageViewer.txt new file mode 100644 index 000000000..10c70601d --- /dev/null +++ b/tdesign-component/example/assets/code/image_viewer._longPressImageViewer.txt @@ -0,0 +1,24 @@ + + Widget _longPressImageViewer(BuildContext context) { + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '长按图片', + onTap: () { + TDImageViewer.showImageViewer( + context: context, + images: images, + deleteBtn: true, + showIndex: true, + onLongPress: (index) { + Navigator.of(context).push(TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: _getSheetItem, + )); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image_viewer._ultraHeightImageViewer.txt b/tdesign-component/example/assets/code/image_viewer._ultraHeightImageViewer.txt new file mode 100644 index 000000000..d1c3205a3 --- /dev/null +++ b/tdesign-component/example/assets/code/image_viewer._ultraHeightImageViewer.txt @@ -0,0 +1,24 @@ + + Widget _ultraHeightImageViewer(BuildContext context) { + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '图片超高情况', + onTap: () { + TDImageViewer.showImageViewer( + context: context, + images: images, + showIndex: true, + width: 180, + onLongPress: (index) { + Navigator.of(context).push(TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: _getSheetItem, + )); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/image_viewer._ultraWidthImageViewer.txt b/tdesign-component/example/assets/code/image_viewer._ultraWidthImageViewer.txt new file mode 100644 index 000000000..ca4cb94af --- /dev/null +++ b/tdesign-component/example/assets/code/image_viewer._ultraWidthImageViewer.txt @@ -0,0 +1,24 @@ + + Widget _ultraWidthImageViewer(BuildContext context) { + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '图片超宽情况', + onTap: () { + TDImageViewer.showImageViewer( + context: context, + images: images, + showIndex: true, + height: 140, + onLongPress: (index) { + Navigator.of(context).push(TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: _getSheetItem, + )); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/indexes._buildOther.txt b/tdesign-component/example/assets/code/indexes._buildOther.txt new file mode 100644 index 000000000..1ce807683 --- /dev/null +++ b/tdesign-component/example/assets/code/indexes._buildOther.txt @@ -0,0 +1,39 @@ + +Widget _buildOther(BuildContext context) { + final renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + final indexList = _list.map((item) => item['index'] as String).toList(); + return TDButton( + text: '胶囊索引', + isBlock: true, + size: TDButtonSize.large, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + onTap: () { + Navigator.of(context).push( + TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.right, + modalTop: renderBox?.size.height, + builder: (context) { + return Container( + color: Colors.white, + child: TDIndexes( + indexList: indexList, + capsuleTheme: true, + builderContent: (context, index) { + final list = _list.firstWhere((element) => element['index'] == index)['children'] as List; + return TDCellGroup( + cells: list + .map((e) => TDCell( + title: e, + )) + .toList(), + ); + }, + ), + ); + }, + ), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/indexes._buildSimple.txt b/tdesign-component/example/assets/code/indexes._buildSimple.txt new file mode 100644 index 000000000..c2563b24d --- /dev/null +++ b/tdesign-component/example/assets/code/indexes._buildSimple.txt @@ -0,0 +1,38 @@ + +Widget _buildSimple(BuildContext context) { + final renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + final indexList = _list.map((item) => item['index'] as String).toList(); + return TDButton( + text: '基础用法', + isBlock: true, + size: TDButtonSize.large, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + onTap: () { + Navigator.of(context).push( + TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.right, + modalTop: renderBox?.size.height, + builder: (context) { + return Container( + color: Colors.white, + child: TDIndexes( + indexList: indexList, + builderContent: (context, index) { + final list = _list.firstWhere((element) => element['index'] == index)['children'] as List; + return TDCellGroup( + cells: list + .map((e) => TDCell( + title: e, + )) + .toList(), + ); + }, + ), + ); + }, + ), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypeAdditionalDesc.txt b/tdesign-component/example/assets/code/input._basicTypeAdditionalDesc.txt new file mode 100644 index 000000000..56cd8401c --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypeAdditionalDesc.txt @@ -0,0 +1,18 @@ + + Widget _basicTypeAdditionalDesc(BuildContext context) { + return TDInput( + type: TDInputType.normal, + leftLabel: '标签文字', + controller: controller[4], + hintText: '请输入文字', + additionInfo: '辅助说明', + backgroundColor: Colors.white, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[4].clear(); + setState(() {}); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypeBasic.txt b/tdesign-component/example/assets/code/input._basicTypeBasic.txt new file mode 100644 index 000000000..5dad0bd31 --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypeBasic.txt @@ -0,0 +1,23 @@ + + Widget _basicTypeBasic(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: 'Label Text', + controller: controller[0], + backgroundColor: Colors.white, + hintText: 'Please enter text', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[0].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypeOptional.txt b/tdesign-component/example/assets/code/input._basicTypeOptional.txt new file mode 100644 index 000000000..972880aab --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypeOptional.txt @@ -0,0 +1,23 @@ + + Widget _basicTypeOptional(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '标签文字', + controller: controller[2], + backgroundColor: Colors.white, + hintText: '请输入文字(选填)', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[2].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypePureInput.txt b/tdesign-component/example/assets/code/input._basicTypePureInput.txt new file mode 100644 index 000000000..a7b86f6d1 --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypePureInput.txt @@ -0,0 +1,22 @@ + + Widget _basicTypePureInput(BuildContext context) { + return Column( + children: [ + TDInput( + controller: controller[3], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[3].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypeRequire.txt b/tdesign-component/example/assets/code/input._basicTypeRequire.txt new file mode 100644 index 000000000..d92a83817 --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypeRequire.txt @@ -0,0 +1,24 @@ + + Widget _basicTypeRequire(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '标签文字', + required: true, + controller: controller[1], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[1].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypeTextLimit.txt b/tdesign-component/example/assets/code/input._basicTypeTextLimit.txt new file mode 100644 index 000000000..c593f6d27 --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypeTextLimit.txt @@ -0,0 +1,26 @@ + + Widget _basicTypeTextLimit(BuildContext context) { + return Column( + children: [ + TDInput( + type: TDInputType.normal, + leftLabel: '标签文字', + controller: controller[5], + hintText: '请输入文字', + maxLength: 10, + additionInfo: '最大输入10个字符', + backgroundColor: Colors.white, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[5].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypeTextLimitChinese2.txt b/tdesign-component/example/assets/code/input._basicTypeTextLimitChinese2.txt new file mode 100644 index 000000000..64e1e43c0 --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypeTextLimitChinese2.txt @@ -0,0 +1,19 @@ + + Widget _basicTypeTextLimitChinese2(BuildContext context) { + return TDInput( + type: TDInputType.normal, + leftLabel: '标签文字', + controller: controller[6], + hintText: '请输入文字', + inputFormatters: [Chinese2Formatter(10)], + additionInfo: '最大输入10个字符,汉字算两个', + backgroundColor: Colors.white, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[6].clear(); + setState(() {}); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypeWithHandleIconOne.txt b/tdesign-component/example/assets/code/input._basicTypeWithHandleIconOne.txt new file mode 100644 index 000000000..c30bb68a6 --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypeWithHandleIconOne.txt @@ -0,0 +1,30 @@ + + Widget _basicTypeWithHandleIconOne(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '标签文字', + controller: controller[7], + backgroundColor: Colors.white, + hintText: '请输入文字', + rightBtn: Icon( + TDIcons.error_circle_filled, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + TDToast.showText('点击右侧按钮', context: context); + }, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[7].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypeWithHandleIconThree.txt b/tdesign-component/example/assets/code/input._basicTypeWithHandleIconThree.txt new file mode 100644 index 000000000..95bc14cbb --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypeWithHandleIconThree.txt @@ -0,0 +1,23 @@ + + Widget _basicTypeWithHandleIconThree(BuildContext context) { + return TDInput( + leftLabel: '标签文字', + controller: controller[9], + backgroundColor: Colors.white, + hintText: '请输入文字', + rightBtn: Icon( + TDIcons.user_avatar, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + TDToast.showText('点击操作按钮', context: context); + }, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[9].clear(); + setState(() {}); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypeWithHandleIconTwo.txt b/tdesign-component/example/assets/code/input._basicTypeWithHandleIconTwo.txt new file mode 100644 index 000000000..f5239660a --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypeWithHandleIconTwo.txt @@ -0,0 +1,34 @@ + + Widget _basicTypeWithHandleIconTwo(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '标签文字', + controller: controller[8], + backgroundColor: Colors.white, + hintText: '请输入文字', + rightBtn: Container( + alignment: Alignment.center, + width: 73, + height: 28, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + color: TDTheme.of(context).brandNormalColor, + ), + child: const TDButton( + text: '操作按钮', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + ), + ), + onBtnTap: () { + TDToast.showText('点击操作按钮', context: context); + }, + needClear: false, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypeWithLeftIcon.txt b/tdesign-component/example/assets/code/input._basicTypeWithLeftIcon.txt new file mode 100644 index 000000000..72a0088a8 --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypeWithLeftIcon.txt @@ -0,0 +1,23 @@ + + Widget _basicTypeWithLeftIcon(BuildContext context) { + return Column( + children: [ + TDInput( + leftIcon: const Icon(TDIcons.app), + controller: controller[11], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[11].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._basicTypeWithLeftIconLeftLabel.txt b/tdesign-component/example/assets/code/input._basicTypeWithLeftIconLeftLabel.txt new file mode 100644 index 000000000..30655a5ec --- /dev/null +++ b/tdesign-component/example/assets/code/input._basicTypeWithLeftIconLeftLabel.txt @@ -0,0 +1,24 @@ + + Widget _basicTypeWithLeftIconLeftLabel(BuildContext context) { + return Column( + children: [ + TDInput( + leftIcon: const Icon(TDIcons.app), + leftLabel: '标签文字', + controller: controller[10], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[10].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._cardStyle.txt b/tdesign-component/example/assets/code/input._cardStyle.txt new file mode 100644 index 000000000..e6773251f --- /dev/null +++ b/tdesign-component/example/assets/code/input._cardStyle.txt @@ -0,0 +1,18 @@ + + Widget _cardStyle(BuildContext context) { + return TDInput( + type: TDInputType.cardStyle, + width: MediaQuery.of(context).size.width - 32, + leftLabel: '标签文字', + controller: controller[21], + hintText: '请输入文字', + backgroundColor: Colors.white, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[21].clear(); + setState(() {}); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._contentCenter.txt b/tdesign-component/example/assets/code/input._contentCenter.txt new file mode 100644 index 000000000..5bd0e23d1 --- /dev/null +++ b/tdesign-component/example/assets/code/input._contentCenter.txt @@ -0,0 +1,24 @@ + + Widget _contentCenter(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '居中', + controller: controller[24], + backgroundColor: Colors.white, + contentAlignment: TextAlign.center, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[24].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._contentLeft.txt b/tdesign-component/example/assets/code/input._contentLeft.txt new file mode 100644 index 000000000..426c93ed8 --- /dev/null +++ b/tdesign-component/example/assets/code/input._contentLeft.txt @@ -0,0 +1,23 @@ + + Widget _contentLeft(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '左对齐', + controller: controller[23], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[23].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._contentPadding.txt b/tdesign-component/example/assets/code/input._contentPadding.txt new file mode 100644 index 000000000..f3fbfa4c3 --- /dev/null +++ b/tdesign-component/example/assets/code/input._contentPadding.txt @@ -0,0 +1,35 @@ + + Widget _contentPadding(BuildContext context) { + var controller = TextEditingController(); + return Container( + color: Colors.yellow, + alignment: Alignment.center, + child: Column( + children: [ + TDInput( + size: TDInputSize.small, + controller: controller, + backgroundColor: Colors.white, + contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10), + hintText: '请输入文字', + ), + TDInput( + type: TDInputType.twoLine, + size: TDInputSize.small, + controller: controller, + backgroundColor: Colors.white, + contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 50), + hintText: '请输入文字', + ), + TDInput( + type: TDInputType.normalMaxTwoLine, + size: TDInputSize.small, + controller: controller, + backgroundColor: Colors.white, + contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 70), + hintText: '请输入文字', + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._contentRight.txt b/tdesign-component/example/assets/code/input._contentRight.txt new file mode 100644 index 000000000..f1e8498fd --- /dev/null +++ b/tdesign-component/example/assets/code/input._contentRight.txt @@ -0,0 +1,24 @@ + + Widget _contentRight(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '右对齐', + controller: controller[25], + backgroundColor: Colors.white, + contentAlignment: TextAlign.end, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[25].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._customHeight.txt b/tdesign-component/example/assets/code/input._customHeight.txt new file mode 100644 index 000000000..48fb49e90 --- /dev/null +++ b/tdesign-component/example/assets/code/input._customHeight.txt @@ -0,0 +1,20 @@ + + Widget _customHeight(BuildContext context) { + var controller = TextEditingController(); + return Container( + color: Colors.yellow, + alignment: Alignment.center, + height: 90, + child: SizedBox( + height: 60, + child: TDInput( + size: TDInputSize.small, + leftLabel: '标签文字', + controller: controller, + backgroundColor: Colors.white, + hintText: '请输入文字', + needClear: true, + ), + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._customLongTextStyle.txt b/tdesign-component/example/assets/code/input._customLongTextStyle.txt new file mode 100644 index 000000000..33d22f3ac --- /dev/null +++ b/tdesign-component/example/assets/code/input._customLongTextStyle.txt @@ -0,0 +1,25 @@ + + Widget _customLongTextStyle(BuildContext context) { + var controller = TextEditingController(); + return Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(top: 16, bottom: 24), + width: MediaQuery.of(context).size.width, + color: Colors.white, + child: TDInput( + type: TDInputType.longText, + cardStyle: TDCardStyle.topText, + width: MediaQuery.of(context).size.width - 32, + cardStyleTopText: '标签文字', + controller: controller, + hintText: '请输入文字', + rightBtn: Icon( + TDIcons.error_circle_filled, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + TDToast.showText('点击右侧按钮', context: context); + } + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._customStyle.txt b/tdesign-component/example/assets/code/input._customStyle.txt new file mode 100644 index 000000000..1eadecd97 --- /dev/null +++ b/tdesign-component/example/assets/code/input._customStyle.txt @@ -0,0 +1,20 @@ + + Widget _customStyle(BuildContext context) { + return TDInput( + leftLabel: '标签文字', + controller: controller[26], + backgroundColor: TDTheme.of(context).grayColor12, + leftLabelStyle: TextStyle(color: TDTheme.of(context).fontWhColor1), + textStyle: TextStyle(color: TDTheme.of(context).fontWhColor1), + hintText: '请输入文字', + hintTextStyle: TextStyle(color: TDTheme.of(context).fontWhColor3), + onChanged: (text) { + setState(() {}); + }, + clearBtnColor: TDTheme.of(context).fontWhColor3, + onClearTap: () { + controller[26].clear(); + setState(() {}); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._hideBottomDivider.txt b/tdesign-component/example/assets/code/input._hideBottomDivider.txt new file mode 100644 index 000000000..b88d03823 --- /dev/null +++ b/tdesign-component/example/assets/code/input._hideBottomDivider.txt @@ -0,0 +1,11 @@ + + Widget _hideBottomDivider(BuildContext context) { + var controller = TextEditingController(); + return TDInput( + leftLabel: '标签文字', + controller: controller, + backgroundColor: Colors.white, + hintText: '请输入文字', + showBottomDivider: false, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._inputStatusAdditionInfo.txt b/tdesign-component/example/assets/code/input._inputStatusAdditionInfo.txt new file mode 100644 index 000000000..0236b0f2c --- /dev/null +++ b/tdesign-component/example/assets/code/input._inputStatusAdditionInfo.txt @@ -0,0 +1,25 @@ + + Widget _inputStatusAdditionInfo(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '标签文字', + controller: controller[17], + backgroundColor: Colors.white, + hintText: '请输入文字', + additionInfo: '错误提示说明', + additionInfoColor: TDTheme.of(context).errorColor6, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[17].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._inputStatusLongInput.txt b/tdesign-component/example/assets/code/input._inputStatusLongInput.txt new file mode 100644 index 000000000..6ea7eda89 --- /dev/null +++ b/tdesign-component/example/assets/code/input._inputStatusLongInput.txt @@ -0,0 +1,14 @@ + + Widget _inputStatusLongInput(BuildContext context) { + return TDInput( + type: TDInputType.normal, + leftLabel: '标签文字', + controller: controller[19], + backgroundColor: Colors.white, + hintText: '输入文字超长不超过两行输入文字超长不超过两行', + hintTextStyle: TextStyle( + color: TDTheme.of(context).fontGyColor1, + ), + maxLines: 2, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._inputStatusLongLabel.txt b/tdesign-component/example/assets/code/input._inputStatusLongLabel.txt new file mode 100644 index 000000000..0548a20b2 --- /dev/null +++ b/tdesign-component/example/assets/code/input._inputStatusLongLabel.txt @@ -0,0 +1,25 @@ + + Widget _inputStatusLongLabel(BuildContext context) { + return Column( + children: [ + TDInput( + leftInfoWidth: 80, + spacer: TDInputSpacer(iconLabelSpace: 4), + leftLabel: '标签超长时最多十个字', + controller: controller[18], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[18].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._inputStatusReadOnly.txt b/tdesign-component/example/assets/code/input._inputStatusReadOnly.txt new file mode 100644 index 000000000..4513473d9 --- /dev/null +++ b/tdesign-component/example/assets/code/input._inputStatusReadOnly.txt @@ -0,0 +1,10 @@ + + Widget _inputStatusReadOnly(BuildContext context) { + return TDInput( + leftLabel: '标签文字', + readOnly: true, + // 不可编辑文字 则不必带入controller + backgroundColor: Colors.white, + hintText: '不可编辑文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._labelOutStyle.txt b/tdesign-component/example/assets/code/input._labelOutStyle.txt new file mode 100644 index 000000000..3d8506a8f --- /dev/null +++ b/tdesign-component/example/assets/code/input._labelOutStyle.txt @@ -0,0 +1,31 @@ + + Widget _labelOutStyle(BuildContext context) { + return Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(top: 16, bottom: 24), + width: MediaQuery.of(context).size.width, + color: Colors.white, + child: TDInput( + type: TDInputType.cardStyle, + cardStyle: TDCardStyle.topText, + width: MediaQuery.of(context).size.width - 32, + cardStyleTopText: '标签文字', + controller: controller[22], + hintText: '请输入文字', + rightBtn: Icon( + TDIcons.error_circle_filled, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + TDToast.showText('点击右侧按钮', context: context); + }, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[22].clear(); + setState(() {}); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._onTapOutside.txt b/tdesign-component/example/assets/code/input._onTapOutside.txt new file mode 100644 index 000000000..2c8397742 --- /dev/null +++ b/tdesign-component/example/assets/code/input._onTapOutside.txt @@ -0,0 +1,23 @@ + + Widget _onTapOutside(BuildContext context) { + var controller = TextEditingController(); + return Container( + color: Colors.yellow, + alignment: Alignment.center, + height: 90, + child: SizedBox( + height: 60, + child: TDInput( + size: TDInputSize.small, + leftLabel: '标签文字', + controller: controller, + backgroundColor: Colors.white, + hintText: '请输入文字', + onTapOutside: (event) { + TDToast.showText('点击输入框外部区域', context: context); + print('on tap outside ${event}'); + }, + ), + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._specialTypeNumber.txt b/tdesign-component/example/assets/code/input._specialTypeNumber.txt new file mode 100644 index 000000000..ca932dcf9 --- /dev/null +++ b/tdesign-component/example/assets/code/input._specialTypeNumber.txt @@ -0,0 +1,12 @@ + + Widget _specialTypeNumber(BuildContext context) { + return TDInput( + type: TDInputType.special, + controller: controller[16], + leftLabel: '数量', + hintText: '填写个数', + backgroundColor: Colors.white, + textAlign: TextAlign.end, + rightWidget: TDText('个', textColor: TDTheme.of(context).fontGyColor1), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._specialTypePassword.txt b/tdesign-component/example/assets/code/input._specialTypePassword.txt new file mode 100644 index 000000000..69ecd9109 --- /dev/null +++ b/tdesign-component/example/assets/code/input._specialTypePassword.txt @@ -0,0 +1,33 @@ + + Widget _specialTypePassword(BuildContext context) { + return Column( + children: [ + TDInput( + type: TDInputType.normal, + controller: controller[12], + obscureText: !browseOn, + leftLabel: '输入密码', + hintText: '请输入密码', + backgroundColor: Colors.white, + rightBtn: browseOn + ? Icon( + TDIcons.browse, + color: TDTheme.of(context).fontGyColor3, + ) + : Icon( + TDIcons.browse_off, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + setState(() { + browseOn = !browseOn; + }); + }, + needClear: false, + ), + const SizedBox( + height: 16, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._specialTypePhoneNumber.txt b/tdesign-component/example/assets/code/input._specialTypePhoneNumber.txt new file mode 100644 index 000000000..d7d417c22 --- /dev/null +++ b/tdesign-component/example/assets/code/input._specialTypePhoneNumber.txt @@ -0,0 +1,49 @@ + + Widget _specialTypePhoneNumber(BuildContext context) { + return Column( + children: [ + TDInput( + type: TDInputType.normal, + controller: controller[14], + leftLabel: '手机号', + hintText: '输入手机号', + backgroundColor: Colors.white, + rightBtn: SizedBox( + width: 98, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(right: 16), + child: Container( + width: 0.5, + height: 24, + color: TDTheme.of(context).grayColor3, + ), + ), + _countdownTime > 0 + ? TDText( + '${countDownText}(${_countdownTime}秒)', + textColor: TDTheme.of(context).fontGyColor4, + ) + : TDText(confirmText, textColor: TDTheme.of(context).brandNormalColor), + ], + ), + ), + needClear: false, + onBtnTap: () { + if (_countdownTime == 0) { + TDToast.showText('点击了发送验证码', context: context); + setState(() { + _countdownTime = 60; + }); + startCountdownTimer(); + } + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._specialTypePrice.txt b/tdesign-component/example/assets/code/input._specialTypePrice.txt new file mode 100644 index 000000000..886df9db7 --- /dev/null +++ b/tdesign-component/example/assets/code/input._specialTypePrice.txt @@ -0,0 +1,19 @@ + + Widget _specialTypePrice(BuildContext context) { + return Column( + children: [ + TDInput( + type: TDInputType.special, + controller: controller[15], + leftLabel: '价格', + hintText: '0.00', + backgroundColor: Colors.white, + textAlign: TextAlign.end, + rightWidget: TDText('元', textColor: TDTheme.of(context).fontGyColor1), + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._specialTypeVerifyCode.txt b/tdesign-component/example/assets/code/input._specialTypeVerifyCode.txt new file mode 100644 index 000000000..d58c65686 --- /dev/null +++ b/tdesign-component/example/assets/code/input._specialTypeVerifyCode.txt @@ -0,0 +1,40 @@ + + Widget _specialTypeVerifyCode(BuildContext context) { + return Column( + children: [ + TDInput( + type: TDInputType.normal, + size: TDInputSize.small, + controller: controller[13], + leftLabel: '验证码', + hintText: '输入验证码', + backgroundColor: Colors.white, + rightBtn: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 0.5, + height: 24, + color: TDTheme.of(context).grayColor3, + ), + const SizedBox( + width: 16, + ), + Image.network( + 'https://img2018.cnblogs.com/blog/736399/202001/736399-20200108170302307-1377487770.jpg', + width: 72, + height: 36, + ) + ], + ), + needClear: false, + onBtnTap: () { + TDToast.showText('点击更换验证码', context: context); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/input._verticalStyle.txt b/tdesign-component/example/assets/code/input._verticalStyle.txt new file mode 100644 index 000000000..03792d921 --- /dev/null +++ b/tdesign-component/example/assets/code/input._verticalStyle.txt @@ -0,0 +1,25 @@ + + Widget _verticalStyle(BuildContext context) { + return TDInput( + spacer: TDInputSpacer(iconLabelSpace: 0), + type: TDInputType.twoLine, + leftLabel: '标签文字', + controller: controller[20], + hintText: '请输入文字', + backgroundColor: Colors.white, + rightBtn: Icon( + TDIcons.error_circle_filled, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + TDToast.showText('点击右侧按钮', context: context); + }, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[20].clear(); + setState(() {}); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/link._basicTypeBasic.txt b/tdesign-component/example/assets/code/link._basicTypeBasic.txt new file mode 100644 index 000000000..150fd91f4 --- /dev/null +++ b/tdesign-component/example/assets/code/link._basicTypeBasic.txt @@ -0,0 +1,9 @@ + + Widget _basicTypeBasic(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLinksWithType(TDLinkType.basic), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/link._buildDisabledLinkStats.txt b/tdesign-component/example/assets/code/link._buildDisabledLinkStats.txt new file mode 100644 index 000000000..32a3ed07d --- /dev/null +++ b/tdesign-component/example/assets/code/link._buildDisabledLinkStats.txt @@ -0,0 +1,4 @@ + + Widget _buildDisabledLinkStats(BuildContext context) { + return _buildLinkWithStyles(TDLinkState.disabled); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/link._buildLinkSizes.txt b/tdesign-component/example/assets/code/link._buildLinkSizes.txt new file mode 100644 index 000000000..68d5a6548 --- /dev/null +++ b/tdesign-component/example/assets/code/link._buildLinkSizes.txt @@ -0,0 +1,15 @@ + + Widget _buildLinkSizes(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildLinkWithSizeAndStyle(TDLinkStyle.primary, TDLinkSize.small), + const SizedBox(height: 48, width: 40), + _buildLinkWithSizeAndStyle(TDLinkStyle.primary, TDLinkSize.medium), + const SizedBox(height: 48, width: 40), + _buildLinkWithSizeAndStyle(TDLinkStyle.primary, TDLinkSize.large), + ], + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/link._buildLinkStats.txt b/tdesign-component/example/assets/code/link._buildLinkStats.txt new file mode 100644 index 000000000..50e6bd7b0 --- /dev/null +++ b/tdesign-component/example/assets/code/link._buildLinkStats.txt @@ -0,0 +1,4 @@ + + Widget _buildLinkStats(BuildContext context) { + return _buildLinkWithStyles(TDLinkState.normal); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/link._withPrefixIcon.txt b/tdesign-component/example/assets/code/link._withPrefixIcon.txt new file mode 100644 index 000000000..0045dde16 --- /dev/null +++ b/tdesign-component/example/assets/code/link._withPrefixIcon.txt @@ -0,0 +1,9 @@ + + Widget _withPrefixIcon(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLinksWithType(TDLinkType.withPrefixIcon), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/link._withSuffixIcon.txt b/tdesign-component/example/assets/code/link._withSuffixIcon.txt new file mode 100644 index 000000000..088803a24 --- /dev/null +++ b/tdesign-component/example/assets/code/link._withSuffixIcon.txt @@ -0,0 +1,9 @@ + + Widget _withSuffixIcon(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLinksWithType(TDLinkType.withSuffixIcon), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/link._withUnderline.txt b/tdesign-component/example/assets/code/link._withUnderline.txt new file mode 100644 index 000000000..d64f8d853 --- /dev/null +++ b/tdesign-component/example/assets/code/link._withUnderline.txt @@ -0,0 +1,9 @@ + + Widget _withUnderline(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLinksWithType(TDLinkType.withUnderline), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/loading._buildCustomSpeedLoading.txt b/tdesign-component/example/assets/code/loading._buildCustomSpeedLoading.txt new file mode 100644 index 000000000..5bafd6456 --- /dev/null +++ b/tdesign-component/example/assets/code/loading._buildCustomSpeedLoading.txt @@ -0,0 +1,34 @@ + + Widget _buildCustomSpeedLoading(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + axis: Axis.horizontal, + text: '加载中…', + duration: _currentSliderValue.round(), + ), + TDSlider(value: _currentSliderValue, + sliderThemeData: TDSliderThemeData( + context: context, + max: 2000, + min: -20, + divisions: 100, + showThumbValue: true, + scaleFormatter: (value) => value.toInt().toString(), + ), + onChanged: (double value) { + setState(() { + _currentSliderValue = value; + }); + },) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/loading._buildLargeLoading.txt b/tdesign-component/example/assets/code/loading._buildLargeLoading.txt new file mode 100644 index 000000000..7d4a60bfd --- /dev/null +++ b/tdesign-component/example/assets/code/loading._buildLargeLoading.txt @@ -0,0 +1,11 @@ + + Widget _buildLargeLoading(BuildContext context) { + return _buildRow([ + const TDLoading( + size: TDLoadingSize.large, + icon: TDLoadingIcon.circle, + text: '加载中…', + axis: Axis.horizontal, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/loading._buildMediumLoading.txt b/tdesign-component/example/assets/code/loading._buildMediumLoading.txt new file mode 100644 index 000000000..06d16d21e --- /dev/null +++ b/tdesign-component/example/assets/code/loading._buildMediumLoading.txt @@ -0,0 +1,11 @@ + + Widget _buildMediumLoading(BuildContext context) { + return _buildRow([ + const TDLoading( + size: TDLoadingSize.medium, + icon: TDLoadingIcon.circle, + text: '加载中…', + axis: Axis.horizontal, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/loading._buildPureIconLoading.txt b/tdesign-component/example/assets/code/loading._buildPureIconLoading.txt new file mode 100644 index 000000000..0cd125a7a --- /dev/null +++ b/tdesign-component/example/assets/code/loading._buildPureIconLoading.txt @@ -0,0 +1,18 @@ + + Widget _buildPureIconLoading(BuildContext context) { + return _buildRow([ + const TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + ), + const TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.activity, + ), + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.point, + iconColor: TDTheme.of(context).brandNormalColor, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/loading._buildPureTextLoading.txt b/tdesign-component/example/assets/code/loading._buildPureTextLoading.txt new file mode 100644 index 000000000..8d2e12fc3 --- /dev/null +++ b/tdesign-component/example/assets/code/loading._buildPureTextLoading.txt @@ -0,0 +1,28 @@ + + Widget _buildPureTextLoading(BuildContext context) { + return _buildRow([ + const TDLoading( + size: TDLoadingSize.small, + text: '加载中…', + ), + TDLoading( + size: TDLoadingSize.small, + text: '加载失败', + textColor: TDTheme.of(context).fontGyColor3, + ), + TDLoading( + size: TDLoadingSize.small, + text: '加载失败', + refreshWidget: GestureDetector( + child: TDText( + '刷新', + font: TDTheme.of(context).fontBodySmall, + textColor: TDTheme.of(context).brandNormalColor, + ), + onTap: () { + TDToast.showText('刷新', context: context); + }, + ), + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/loading._buildRow.txt b/tdesign-component/example/assets/code/loading._buildRow.txt new file mode 100644 index 000000000..8cc11c69d --- /dev/null +++ b/tdesign-component/example/assets/code/loading._buildRow.txt @@ -0,0 +1,8 @@ + + Widget _buildRow(List list) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row(children: list.fold([], (previousValue, element) => [...previousValue, element, rowSpace])),)); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/loading._buildSmallLoading.txt b/tdesign-component/example/assets/code/loading._buildSmallLoading.txt new file mode 100644 index 000000000..a2c221813 --- /dev/null +++ b/tdesign-component/example/assets/code/loading._buildSmallLoading.txt @@ -0,0 +1,11 @@ + + Widget _buildSmallLoading(BuildContext context) { + return _buildRow([ + const TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + text: '加载中…', + axis: Axis.horizontal, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/loading._buildTextIconHorizontalLoading.txt b/tdesign-component/example/assets/code/loading._buildTextIconHorizontalLoading.txt new file mode 100644 index 000000000..6bd0bb69b --- /dev/null +++ b/tdesign-component/example/assets/code/loading._buildTextIconHorizontalLoading.txt @@ -0,0 +1,17 @@ + + Widget _buildTextIconHorizontalLoading(BuildContext context) { + return _buildRow(const [ + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + text: '加载中…', + axis: Axis.horizontal, + ), + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.activity, + text: '加载中…', + axis: Axis.horizontal, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/loading._buildTextIconVerticalLoading.txt b/tdesign-component/example/assets/code/loading._buildTextIconVerticalLoading.txt new file mode 100644 index 000000000..157389189 --- /dev/null +++ b/tdesign-component/example/assets/code/loading._buildTextIconVerticalLoading.txt @@ -0,0 +1,17 @@ + + Widget _buildTextIconVerticalLoading(BuildContext context) { + return _buildRow(const [ + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + text: '加载中…', + axis: Axis.vertical, + ), + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.activity, + text: '加载中…', + axis: Axis.vertical, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/message._buildErrorMessage.txt b/tdesign-component/example/assets/code/message._buildErrorMessage.txt new file mode 100644 index 000000000..2b54c710b --- /dev/null +++ b/tdesign-component/example/assets/code/message._buildErrorMessage.txt @@ -0,0 +1,20 @@ + + Widget _buildErrorMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '错误通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.error, + duration: 3000, + ); + }); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/message._buildIconTextMessage.txt b/tdesign-component/example/assets/code/message._buildIconTextMessage.txt new file mode 100644 index 000000000..40c1861eb --- /dev/null +++ b/tdesign-component/example/assets/code/message._buildIconTextMessage.txt @@ -0,0 +1,20 @@ + + Widget _buildIconTextMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '带图标的通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + content: _commonContent, + visible: true, + icon: true, + theme: MessageTheme.info, + duration: 3000, + ); + }); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/message._buildInfoMessage.txt b/tdesign-component/example/assets/code/message._buildInfoMessage.txt new file mode 100644 index 000000000..02b57a378 --- /dev/null +++ b/tdesign-component/example/assets/code/message._buildInfoMessage.txt @@ -0,0 +1,21 @@ + + Widget _buildInfoMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '普通通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.info, + duration: 3000, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/message._buildLinkMessage.txt b/tdesign-component/example/assets/code/message._buildLinkMessage.txt new file mode 100644 index 000000000..0e68c84fc --- /dev/null +++ b/tdesign-component/example/assets/code/message._buildLinkMessage.txt @@ -0,0 +1,27 @@ + + Widget _buildLinkMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '带按钮的通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.info, + duration: 3000, + link: MessageLink( + name: '按钮', + uri: Uri.parse('https://tdesign.tencent.com/'), + ), + // link: '按钮', + onLinkClick: () { + print('link clicked!'); + }); + }); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/message._buildMessageWithCloseButton.txt b/tdesign-component/example/assets/code/message._buildMessageWithCloseButton.txt new file mode 100644 index 000000000..3da4c175b --- /dev/null +++ b/tdesign-component/example/assets/code/message._buildMessageWithCloseButton.txt @@ -0,0 +1,25 @@ + + Widget _buildMessageWithCloseButton(BuildContext context) { + return TDButton( + isBlock: true, + text: '带关闭的通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.info, + duration: 300000, + closeBtn: true, + link: MessageLink(name: '按钮', uri: Uri.parse('www.example.com')), + onCloseBtnClick: () { + print('Close button clicked!'); + }, + ); + }); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/message._buildPlainTextMessage.txt b/tdesign-component/example/assets/code/message._buildPlainTextMessage.txt new file mode 100644 index 000000000..d199ef5d1 --- /dev/null +++ b/tdesign-component/example/assets/code/message._buildPlainTextMessage.txt @@ -0,0 +1,24 @@ + + Widget _buildPlainTextMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '纯文字的通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + content: _commonContent, + visible: true, + icon: false, + theme: MessageTheme.info, + duration: 3000, + onDurationEnd: () { + print('message end'); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/message._buildRollingMessage.txt b/tdesign-component/example/assets/code/message._buildRollingMessage.txt new file mode 100644 index 000000000..a935a353c --- /dev/null +++ b/tdesign-component/example/assets/code/message._buildRollingMessage.txt @@ -0,0 +1,23 @@ + + Widget _buildRollingMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '可滚动的通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: false, + marquee: MessageMarquee(speed: 5000, loop: 1, delay: 300), + content: longContent, + theme: MessageTheme.info, + duration: 8000, + onCloseBtnClick: () { + print('Close button clicked!'); + }); + }); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/message._buildSuccessMessage.txt b/tdesign-component/example/assets/code/message._buildSuccessMessage.txt new file mode 100644 index 000000000..4c7de921f --- /dev/null +++ b/tdesign-component/example/assets/code/message._buildSuccessMessage.txt @@ -0,0 +1,21 @@ + + Widget _buildSuccessMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '成功通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.success, + duration: 3000, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/message._buildWarningMessage.txt b/tdesign-component/example/assets/code/message._buildWarningMessage.txt new file mode 100644 index 000000000..08d6b8a0d --- /dev/null +++ b/tdesign-component/example/assets/code/message._buildWarningMessage.txt @@ -0,0 +1,20 @@ + + Widget _buildWarningMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '警示通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.warning, + duration: 3000, + ); + }); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/navbar._baseH5Navbar.txt b/tdesign-component/example/assets/code/navbar._baseH5Navbar.txt new file mode 100644 index 000000000..60d4ce7d5 --- /dev/null +++ b/tdesign-component/example/assets/code/navbar._baseH5Navbar.txt @@ -0,0 +1,10 @@ + + Widget _baseH5Navbar(BuildContext context) { + return const TDNavBar( + height: 48, + titleFontWeight: FontWeight.w600, + title: titleText, + screenAdaptation: false, + useDefaultBack: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/navbar._leftMultiAction.txt b/tdesign-component/example/assets/code/navbar._leftMultiAction.txt new file mode 100644 index 000000000..8c7abc35a --- /dev/null +++ b/tdesign-component/example/assets/code/navbar._leftMultiAction.txt @@ -0,0 +1,19 @@ + + Widget _leftMultiAction(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 16), + child: TDNavBar( + height: 48, + title: titleText, + titleFontWeight: FontWeight.w600, + screenAdaptation: false, + useDefaultBack: true, + leftBarItems: [ + TDNavBarItem(icon: TDIcons.close, iconSize: 24), + ], + rightBarItems: [ + TDNavBarItem(icon: TDIcons.ellipsis, iconSize: 24) + ] + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/navbar._logoNavbar.txt b/tdesign-component/example/assets/code/navbar._logoNavbar.txt new file mode 100644 index 000000000..ae187af02 --- /dev/null +++ b/tdesign-component/example/assets/code/navbar._logoNavbar.txt @@ -0,0 +1,18 @@ + + Widget _logoNavbar(BuildContext context){ + return TDNavBar( + useDefaultBack: false, + screenAdaptation: false, + centerTitle: false, + titleMargin: 0, + titleWidget: const TDImage( + assetUrl: 'assets/img/td_brand.png', + width: 102, + height: 24, + ), + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/navbar._rightMultiAction.txt b/tdesign-component/example/assets/code/navbar._rightMultiAction.txt new file mode 100644 index 000000000..40d7ffd43 --- /dev/null +++ b/tdesign-component/example/assets/code/navbar._rightMultiAction.txt @@ -0,0 +1,17 @@ + + Widget _rightMultiAction(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 16), + child: TDNavBar( + height: 48, + title: titleText, + titleFontWeight: FontWeight.w600, + screenAdaptation: false, + useDefaultBack: true, + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home, iconSize: 24, ), + TDNavBarItem(icon: TDIcons.ellipsis, iconSize: 24,) + ] + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/navbar._searchNavbar.txt b/tdesign-component/example/assets/code/navbar._searchNavbar.txt new file mode 100644 index 000000000..20e250994 --- /dev/null +++ b/tdesign-component/example/assets/code/navbar._searchNavbar.txt @@ -0,0 +1,24 @@ + + Widget _searchNavbar(BuildContext context){ + return TDNavBar( + useDefaultBack: false, + screenAdaptation: false, + centerTitle: false, + titleMargin: 0, + titleWidget: TDSearchBar( + needCancel: false, + autoHeight: true, + padding: const EdgeInsets.fromLTRB(0, 2, 0, 2), + placeHolder: '搜索预设文案', + mediumStyle: true, + style: TDSearchStyle.round, + onTextChanged: (String text) { + print('input:$text'); + }, + ), + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/navbar._setBgColorNavbar.txt b/tdesign-component/example/assets/code/navbar._setBgColorNavbar.txt new file mode 100644 index 000000000..c516b2564 --- /dev/null +++ b/tdesign-component/example/assets/code/navbar._setBgColorNavbar.txt @@ -0,0 +1,19 @@ + + Widget _setBgColorNavbar(BuildContext context) { + return TDNavBar( + height: 48, + title: titleText, + titleColor: Colors.white, + backgroundColor: TDTheme.of(context).brandNormalColor, + titleFontWeight: FontWeight.w600, + useDefaultBack: false, + screenAdaptation: false, + leftBarItems: [ + TDNavBarItem(icon: TDIcons.chevron_left, iconSize: 24, iconColor: Colors.white), + ], + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home, iconSize: 24, iconColor: Colors.white), + TDNavBarItem(icon: TDIcons.ellipsis, iconSize: 24, iconColor: Colors.white) + ] + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/navbar._shadowNavbar.txt b/tdesign-component/example/assets/code/navbar._shadowNavbar.txt new file mode 100644 index 000000000..cbcc7e006 --- /dev/null +++ b/tdesign-component/example/assets/code/navbar._shadowNavbar.txt @@ -0,0 +1,17 @@ + + Widget _shadowNavbar(BuildContext context) { + return TDNavBar( + height: 48, + titleFontWeight: FontWeight.w600, + title: titleText, + screenAdaptation: false, + useDefaultBack: true, + boxShadow: [ + BoxShadow( + blurRadius: 4, + offset: const Offset(0, 4), + color: TDTheme.of(context).grayColor5, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/navbar._titleBelowNavbar.txt b/tdesign-component/example/assets/code/navbar._titleBelowNavbar.txt new file mode 100644 index 000000000..c67d6a72a --- /dev/null +++ b/tdesign-component/example/assets/code/navbar._titleBelowNavbar.txt @@ -0,0 +1,27 @@ + + Widget _titleBelowNavbar(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 16), + child: TDNavBar( + height: 104, + title: '返回', + titleColor: const Color.fromRGBO(0, 0, 0, 0.9), + belowTitleWidget: SizedBox( + height: 56, + child: TDText(titleText, font: Font(size: 28, lineHeight: 52), fontWeight: FontWeight.w600,), + ), + titleFont: Font(size: 16, lineHeight: 24), + centerTitle: false, + titleMargin: 0, + screenAdaptation: false, + useDefaultBack: false, + leftBarItems: [ + TDNavBarItem(icon: TDIcons.chevron_left,iconSize: 24), + ], + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/navbar._titleCenterNavbar.txt b/tdesign-component/example/assets/code/navbar._titleCenterNavbar.txt new file mode 100644 index 000000000..26ed7078f --- /dev/null +++ b/tdesign-component/example/assets/code/navbar._titleCenterNavbar.txt @@ -0,0 +1,14 @@ + + Widget _titleCenterNavbar(BuildContext context) { + return TDNavBar( + height: 48, + title: titleText, + titleFontWeight: FontWeight.w600, + screenAdaptation: false, + useDefaultBack: true, + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/navbar._titleLeftNavbar.txt b/tdesign-component/example/assets/code/navbar._titleLeftNavbar.txt new file mode 100644 index 000000000..d988a63db --- /dev/null +++ b/tdesign-component/example/assets/code/navbar._titleLeftNavbar.txt @@ -0,0 +1,19 @@ + + Widget _titleLeftNavbar(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 16), + child: TDNavBar( + height: 48, + title: titleText, + titleFontWeight: FontWeight.w600, + centerTitle: false, + titleMargin: 0, + screenAdaptation: false, + useDefaultBack: true, + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/navbar._titleNormalNavbar.txt b/tdesign-component/example/assets/code/navbar._titleNormalNavbar.txt new file mode 100644 index 000000000..4b3d43304 --- /dev/null +++ b/tdesign-component/example/assets/code/navbar._titleNormalNavbar.txt @@ -0,0 +1,14 @@ + + Widget _titleNormalNavbar(BuildContext context) { + return TDNavBar( + height: 48, + title: titleText, + titleFontWeight: FontWeight.w600, + screenAdaptation: false, + useDefaultBack: true, + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._cardNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._cardNoticeBar.txt new file mode 100644 index 000000000..df57b978e --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._cardNoticeBar.txt @@ -0,0 +1,54 @@ + +Widget _cardNoticeBar(BuildContext context) { + var size = MediaQuery.of(context).size; + return Container( + margin: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: TDNoticeBarStyle.generateTheme(context).backgroundColor, + borderRadius: const BorderRadius.all(Radius.circular(9)), + boxShadow: const [ + BoxShadow( + color: Color(0x0d000000), + blurRadius: 8, + spreadRadius: 2, + offset: Offset(0, 2), + ), + BoxShadow( + color: Color(0x0f000000), + blurRadius: 10, + spreadRadius: 1, + offset: Offset(0, 8), + ), + BoxShadow( + color: Color(0x1a000000), + blurRadius: 5, + spreadRadius: -3, + offset: Offset(0, 5), + ), + ], + ), + child: Column( + children: [ + Container( + width: size.width - 32, + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + clipBehavior: Clip.hardEdge, + child: const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.chevron_right, + ), + ), + Container( + height: 150, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + ) + ], + ), + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._closeNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._closeNoticeBar.txt new file mode 100644 index 000000000..72079a158 --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._closeNoticeBar.txt @@ -0,0 +1,8 @@ + +Widget _closeNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.close, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._customNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._customNoticeBar.txt new file mode 100644 index 000000000..90bf32b21 --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._customNoticeBar.txt @@ -0,0 +1,9 @@ + +Widget _customNoticeBar(BuildContext context) { + return TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.notification, + suffixIcon: TDIcons.chevron_right, + style: TDNoticeBarStyle(backgroundColor: TDTheme.of(context).grayColor3), + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._entranceNoticeBar1.txt b/tdesign-component/example/assets/code/noticeBar._entranceNoticeBar1.txt new file mode 100644 index 000000000..500443ec4 --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._entranceNoticeBar1.txt @@ -0,0 +1,15 @@ + +Widget _entranceNoticeBar1(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + right: TDButton( + text: '文字按钮', + type: TDButtonType.text, + theme: TDButtonTheme.primary, + size: TDButtonSize.extraSmall, + height: 22, + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0), + ), + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._entranceNoticeBar2.txt b/tdesign-component/example/assets/code/noticeBar._entranceNoticeBar2.txt new file mode 100644 index 000000000..40d2a5bc6 --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._entranceNoticeBar2.txt @@ -0,0 +1,11 @@ + +Widget _entranceNoticeBar2(BuildContext context) { + return const Padding( + padding: EdgeInsets.only(top: 16), + child: TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.chevron_right, + ), + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._errorNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._errorNoticeBar.txt new file mode 100644 index 000000000..831330e17 --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._errorNoticeBar.txt @@ -0,0 +1,8 @@ + +Widget _errorNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.error, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._iconNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._iconNoticeBar.txt new file mode 100644 index 000000000..65ea94bb3 --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._iconNoticeBar.txt @@ -0,0 +1,7 @@ + +Widget _iconNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._leftNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._leftNoticeBar.txt new file mode 100644 index 000000000..f8e0d026b --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._leftNoticeBar.txt @@ -0,0 +1,15 @@ + +Widget _leftNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + suffixIcon: TDIcons.chevron_right, + left: TDButton( + text: '文本', + type: TDButtonType.text, + theme: TDButtonTheme.primary, + size: TDButtonSize.extraSmall, + height: 22, + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0), + ), + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._normalNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._normalNoticeBar.txt new file mode 100644 index 000000000..1fd7bd8d3 --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._normalNoticeBar.txt @@ -0,0 +1,8 @@ + +Widget _normalNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.info, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._scrollIconNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._scrollIconNoticeBar.txt new file mode 100644 index 000000000..ad9666ec4 --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._scrollIconNoticeBar.txt @@ -0,0 +1,12 @@ + +Widget _scrollIconNoticeBar(BuildContext context) { + return const Padding( + padding: EdgeInsets.only(top: 16), + child: TDNoticeBar( + context: '提示文字描述提示文字描述提示文字描述提示文字描述提示文字', + speed: 50, + prefixIcon: TDIcons.sound, + marquee: true, + ), + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._scrollNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._scrollNoticeBar.txt new file mode 100644 index 000000000..ebdb515c8 --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._scrollNoticeBar.txt @@ -0,0 +1,8 @@ + +Widget _scrollNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '提示文字描述提示文字描述提示文字描述提示文字描述提示文字', + marquee: true, + speed: 50, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._stepNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._stepNoticeBar.txt new file mode 100644 index 000000000..ff29ca57e --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._stepNoticeBar.txt @@ -0,0 +1,9 @@ + +Widget _stepNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: ['君不见黄河之水天上来', '奔流到海不复回', '君不见'], + direction: Axis.vertical, + prefixIcon: TDIcons.sound, + marquee: true, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._successNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._successNoticeBar.txt new file mode 100644 index 000000000..227263ceb --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._successNoticeBar.txt @@ -0,0 +1,8 @@ + +Widget _successNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.success, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._tapNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._tapNoticeBar.txt new file mode 100644 index 000000000..b5f6a0b43 --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._tapNoticeBar.txt @@ -0,0 +1,11 @@ + +Widget _tapNoticeBar(BuildContext context) { + return TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.chevron_right, + onTap: (trigger) { + TDToast.showText('tap:$trigger', context: context); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._textNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._textNoticeBar.txt new file mode 100644 index 000000000..a68bb06bb --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._textNoticeBar.txt @@ -0,0 +1,4 @@ + +Widget _textNoticeBar(BuildContext context) { + return const TDNoticeBar(context: '这是一条普通的通知信息'); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/noticeBar._warningNoticeBar.txt b/tdesign-component/example/assets/code/noticeBar._warningNoticeBar.txt new file mode 100644 index 000000000..34b9137d4 --- /dev/null +++ b/tdesign-component/example/assets/code/noticeBar._warningNoticeBar.txt @@ -0,0 +1,8 @@ + +Widget _warningNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.warning, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/picker.buildArea.txt b/tdesign-component/example/assets/code/picker.buildArea.txt new file mode 100644 index 000000000..8958167d8 --- /dev/null +++ b/tdesign-component/example/assets/code/picker.buildArea.txt @@ -0,0 +1,15 @@ + + Widget buildArea(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiPicker(context, title: '选择地区', + onConfirm: (selected) { + setState(() { + selected_1 = '${data_1[selected[0]]}'; + }); + Navigator.of(context).pop(); + }, data: [data_1]); + }, + child: buildSelectRow(context, selected_1, '选择地区'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/picker.buildAreaWithTitle.txt b/tdesign-component/example/assets/code/picker.buildAreaWithTitle.txt new file mode 100644 index 000000000..347354e02 --- /dev/null +++ b/tdesign-component/example/assets/code/picker.buildAreaWithTitle.txt @@ -0,0 +1,15 @@ + + Widget buildAreaWithTitle(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiPicker(context, title: '选择地区', + onConfirm: (selected) { + setState(() { + selected_4 = '${data_1[selected[0]]}'; + }); + Navigator.of(context).pop(); + }, data: [data_1]); + }, + child: buildSelectRow(context, selected_4, '带标题选择器'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/picker.buildAreaWithoutTitle.txt b/tdesign-component/example/assets/code/picker.buildAreaWithoutTitle.txt new file mode 100644 index 000000000..2f32c01c4 --- /dev/null +++ b/tdesign-component/example/assets/code/picker.buildAreaWithoutTitle.txt @@ -0,0 +1,15 @@ + + Widget buildAreaWithoutTitle(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiPicker(context, title: '', + onConfirm: (selected) { + setState(() { + selected_5 = '${data_1[selected[0]]}'; + }); + Navigator.of(context).pop(); + }, data: [data_1]); + }, + child: buildSelectRow(context, selected_5, '无标题选择器'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/picker.buildCustomLeftRightText.txt b/tdesign-component/example/assets/code/picker.buildCustomLeftRightText.txt new file mode 100644 index 000000000..ff0ec571c --- /dev/null +++ b/tdesign-component/example/assets/code/picker.buildCustomLeftRightText.txt @@ -0,0 +1,35 @@ + + Widget buildCustomLeftRightText(BuildContext context) { + return Column( + children: [ + GestureDetector( + onTap: () { + TDPicker.showMultiPicker(context, + leftText: '自定义取消', + rightText: '自定义确认', + title: '基础选择器', onConfirm: (selected) { + setState(() { + selected_5 = '${data_1[selected[0]]}'; + }); + Navigator.of(context).pop(); + }, data: [data_1]); + }, + child: buildSelectRow(context, selected_5, '基础选择器'), + ), + GestureDetector( + onTap: () { + TDPicker.showMultiLinkedPicker(context, + leftText: '自定义取消', + rightText: '自定义确认', + title: '联动选择器', onConfirm: (selected) { + setState(() { + selected_3 = '${selected[0]} ${selected[1]} ${selected[2]}'; + }); + Navigator.of(context).pop(); + }, data: data_3, columnNum: 3, initialData: ['浙江省', '杭州市', '西湖区']); + }, + child: buildSelectRow(context, selected_3, '联动选择器'), + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/picker.buildKeepMultiArea.txt b/tdesign-component/example/assets/code/picker.buildKeepMultiArea.txt new file mode 100644 index 000000000..7d119b5bc --- /dev/null +++ b/tdesign-component/example/assets/code/picker.buildKeepMultiArea.txt @@ -0,0 +1,19 @@ + + Widget buildKeepMultiArea(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiLinkedPicker(context, title: '选择地区', + onConfirm: (selected) { + setState(() { + selected_3 = '${selected[0]} ${selected[1]} ${selected[2]}'; + }); + Navigator.of(context).pop(); + }, + data: data_3, + columnNum: 3, + keepSameSelection: true, + initialData: ['广东省', '深圳市', '罗湖区']); + }, + child: buildSelectRow(context, selected_3, '选择地区'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/picker.buildMultiArea.txt b/tdesign-component/example/assets/code/picker.buildMultiArea.txt new file mode 100644 index 000000000..e6f339a43 --- /dev/null +++ b/tdesign-component/example/assets/code/picker.buildMultiArea.txt @@ -0,0 +1,18 @@ + + Widget buildMultiArea(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiLinkedPicker(context, title: '选择地区', + onConfirm: (selected) { + setState(() { + selected_3 = '${selected[0]} ${selected[1]} ${selected[2]}'; + }); + Navigator.of(context).pop(); + }, + data: data_3, + columnNum: 3, + initialData: ['浙江省', '杭州市', '西湖区']); + }, + child: buildSelectRow(context, selected_3, '选择地区'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/picker.buildTime.txt b/tdesign-component/example/assets/code/picker.buildTime.txt new file mode 100644 index 000000000..810e46c2b --- /dev/null +++ b/tdesign-component/example/assets/code/picker.buildTime.txt @@ -0,0 +1,15 @@ + + Widget buildTime(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiPicker(context, title: '选择时间', + onConfirm: (selected) { + setState(() { + selected_2 = '${data_2[0][selected[0]]} ${data_2[1][selected[1]]}'; + }); + Navigator.of(context).pop(); + }, data: data_2); + }, + child: buildSelectRow(context, selected_2, '选择时间'), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildBottomLeftPopover.txt b/tdesign-component/example/assets/code/popover._buildBottomLeftPopover.txt new file mode 100644 index 000000000..75c9d616a --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildBottomLeftPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildBottomLeftPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '底部左', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.bottomLeft, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildBottomPopover.txt b/tdesign-component/example/assets/code/popover._buildBottomPopover.txt new file mode 100644 index 000000000..e40480071 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildBottomPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildBottomPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '底部中', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.bottom, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildBottomRightPopover.txt b/tdesign-component/example/assets/code/popover._buildBottomRightPopover.txt new file mode 100644 index 000000000..e4bffbc72 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildBottomRightPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildBottomRightPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '底部右', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.bottomRight, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildDarkPopover.txt b/tdesign-component/example/assets/code/popover._buildDarkPopover.txt new file mode 100644 index 000000000..551b0fe12 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildDarkPopover.txt @@ -0,0 +1,24 @@ + + Widget _buildDarkPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '深色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildErrorPopover.txt b/tdesign-component/example/assets/code/popover._buildErrorPopover.txt new file mode 100644 index 000000000..b99158a8e --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildErrorPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildErrorPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '错误色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + theme: TDPopoverTheme.error, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildInfoPopover.txt b/tdesign-component/example/assets/code/popover._buildInfoPopover.txt new file mode 100644 index 000000000..1bb3f8718 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildInfoPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildInfoPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '品牌色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + theme: TDPopoverTheme.info, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildLeftBottomPopover.txt b/tdesign-component/example/assets/code/popover._buildLeftBottomPopover.txt new file mode 100644 index 000000000..f50121436 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildLeftBottomPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildLeftBottomPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '左侧下', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.leftBottom, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildLeftPopover.txt b/tdesign-component/example/assets/code/popover._buildLeftPopover.txt new file mode 100644 index 000000000..535c24f26 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildLeftPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildLeftPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '左侧中', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.left, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildLeftTopPopover.txt b/tdesign-component/example/assets/code/popover._buildLeftTopPopover.txt new file mode 100644 index 000000000..b54ddafea --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildLeftTopPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildLeftTopPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '左侧上', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.leftTop, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildLightPopover.txt b/tdesign-component/example/assets/code/popover._buildLightPopover.txt new file mode 100644 index 000000000..82ed28111 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildLightPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildLightPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '浅色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + theme: TDPopoverTheme.light, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildMultiLinePopover.txt b/tdesign-component/example/assets/code/popover._buildMultiLinePopover.txt new file mode 100644 index 000000000..1bb5d0805 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildMultiLinePopover.txt @@ -0,0 +1,25 @@ + + Widget _buildMultiLinePopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '多行内容', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + width: 200, + content: '弹出气泡内容弹出气泡内容弹出气泡内容弹出气泡内容', + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildNCustomPopover.txt b/tdesign-component/example/assets/code/popover._buildNCustomPopover.txt new file mode 100644 index 000000000..9ce7eb79a --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildNCustomPopover.txt @@ -0,0 +1,22 @@ + + Widget _buildNCustomPopover(BuildContext context) { + return LayoutBuilder( + builder: (_, constrains) { + return TDButton( + text: '自定义内容', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + padding: const EdgeInsets.all(0), + width: 108, + height: 148, + contentWidget: _buildPopoverList(context), + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildNoArrowPopover.txt b/tdesign-component/example/assets/code/popover._buildNoArrowPopover.txt new file mode 100644 index 000000000..757669716 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildNoArrowPopover.txt @@ -0,0 +1,18 @@ + + Widget _buildNoArrowPopover(BuildContext context) { + return LayoutBuilder( + builder: (_, constrains) { + return TDButton( + size: TDButtonSize.medium, + text: '不带箭头', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, content: '弹出气泡内容', showArrow: false); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildPopover.txt b/tdesign-component/example/assets/code/popover._buildPopover.txt new file mode 100644 index 000000000..cc0574d59 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildPopover.txt @@ -0,0 +1,20 @@ + + Widget _buildPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '带箭头', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover(context: _, content: '弹出气泡内容'); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildRightBottomPopover.txt b/tdesign-component/example/assets/code/popover._buildRightBottomPopover.txt new file mode 100644 index 000000000..b00139bf0 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildRightBottomPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildRightBottomPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '右侧下', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.rightBottom, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildRightPopover.txt b/tdesign-component/example/assets/code/popover._buildRightPopover.txt new file mode 100644 index 000000000..21e3e683d --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildRightPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildRightPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '右侧中', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.right, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildRightTopPopover.txt b/tdesign-component/example/assets/code/popover._buildRightTopPopover.txt new file mode 100644 index 000000000..136f964ea --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildRightTopPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildRightTopPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '右侧上', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.rightTop, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildSuccessPopover.txt b/tdesign-component/example/assets/code/popover._buildSuccessPopover.txt new file mode 100644 index 000000000..8bb53c779 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildSuccessPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildSuccessPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '成功色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + theme: TDPopoverTheme.success, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildTopLeftPopover.txt b/tdesign-component/example/assets/code/popover._buildTopLeftPopover.txt new file mode 100644 index 000000000..c1eed2f49 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildTopLeftPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildTopLeftPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '顶部左', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.topLeft, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildTopPopover.txt b/tdesign-component/example/assets/code/popover._buildTopPopover.txt new file mode 100644 index 000000000..5f066ae02 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildTopPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildTopPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '顶部中', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.top, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildTopRightPopover.txt b/tdesign-component/example/assets/code/popover._buildTopRightPopover.txt new file mode 100644 index 000000000..45d58f420 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildTopRightPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildTopRightPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '顶部右', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.topRight, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popover._buildWarningPopover.txt b/tdesign-component/example/assets/code/popover._buildWarningPopover.txt new file mode 100644 index 000000000..06985b640 --- /dev/null +++ b/tdesign-component/example/assets/code/popover._buildWarningPopover.txt @@ -0,0 +1,25 @@ + + Widget _buildWarningPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '警告色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + theme: TDPopoverTheme.warning, + ); + }, + ); + }, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottom.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottom.txt new file mode 100644 index 000000000..7fcc18d43 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromBottom.txt @@ -0,0 +1,21 @@ + + Widget _buildPopFromBottom(BuildContext context) { + return TDButton( + text: '底部弹出', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return Container( + color: Colors.white, + height: 240, + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithClose.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithClose.txt new file mode 100644 index 000000000..e2faa2305 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithClose.txt @@ -0,0 +1,25 @@ + + Widget _buildPopFromBottomWithClose(BuildContext context) { + return TDButton( + text: '底部弹出层-带关闭', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + closeClick: () { + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndLeftTitle.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndLeftTitle.txt new file mode 100644 index 000000000..b0dc6a6f3 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndLeftTitle.txt @@ -0,0 +1,27 @@ + + Widget _buildPopFromBottomWithCloseAndLeftTitle(BuildContext context) { + return TDButton( + text: '底部弹出层-带左边标题及关闭', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: '标题文字', + titleLeft: true, + closeClick: () { + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndTitle.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndTitle.txt new file mode 100644 index 000000000..41a0602d5 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithCloseAndTitle.txt @@ -0,0 +1,26 @@ + + Widget _buildPopFromBottomWithCloseAndTitle(BuildContext context) { + return TDButton( + text: '底部弹出层-带标题及关闭', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: '标题文字', + closeClick: () { + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperation.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperation.txt new file mode 100644 index 000000000..6a12a0cf7 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperation.txt @@ -0,0 +1,29 @@ + + Widget _buildPopFromBottomWithOperation(BuildContext context) { + return TDButton( + text: '底部弹出层-带操作', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomConfirmPanel( + leftClick: () { + Navigator.maybePop(context); + }, + rightClick: () { + TDToast.showText('确定', context: context); + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperationAndTitle.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperationAndTitle.txt new file mode 100644 index 000000000..053b9ee95 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithOperationAndTitle.txt @@ -0,0 +1,30 @@ + + Widget _buildPopFromBottomWithOperationAndTitle(BuildContext context) { + return TDButton( + text: '底部弹出层-带标题及操作', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomConfirmPanel( + title: '标题文字', + leftClick: () { + Navigator.maybePop(context); + }, + rightClick: () { + TDToast.showText('确定', context: context); + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromBottomWithTitle.txt b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithTitle.txt new file mode 100644 index 000000000..360667f6c --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromBottomWithTitle.txt @@ -0,0 +1,27 @@ + + Widget _buildPopFromBottomWithTitle(BuildContext context) { + return TDButton( + text: '底部弹出层-仅标题', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: '标题文字', + hideClose: true, + // closeClick: () { + // Navigator.maybePop(context); + // }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromCenter.txt b/tdesign-component/example/assets/code/popup._buildPopFromCenter.txt new file mode 100644 index 000000000..0cabcdde7 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromCenter.txt @@ -0,0 +1,22 @@ + + Widget _buildPopFromCenter(BuildContext context) { + return TDButton( + text: '中间弹出', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.center, + builder: (context) { + return Container( + color: Colors.white, + width: 240, + height: 240, + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromCenterWithClose.txt b/tdesign-component/example/assets/code/popup._buildPopFromCenterWithClose.txt new file mode 100644 index 000000000..e43533c15 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromCenterWithClose.txt @@ -0,0 +1,27 @@ + + Widget _buildPopFromCenterWithClose(BuildContext context) { + return TDButton( + text: '居中弹出层-带关闭', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + isDismissible: false, + slideTransitionFrom: SlideTransitionFrom.center, + builder: (context) { + return TDPopupCenterPanel( + closeClick: () { + Navigator.maybePop(context); + }, + child: const SizedBox( + width: 240, + height: 240, + ), + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromCenterWithUnderClose.txt b/tdesign-component/example/assets/code/popup._buildPopFromCenterWithUnderClose.txt new file mode 100644 index 000000000..d9faf665f --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromCenterWithUnderClose.txt @@ -0,0 +1,28 @@ + + Widget _buildPopFromCenterWithUnderClose(BuildContext context) { + return TDButton( + text: '居中弹出层-关闭在下方', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + isDismissible: false, + slideTransitionFrom: SlideTransitionFrom.center, + builder: (context) { + return TDPopupCenterPanel( + closeUnderBottom: true, + closeClick: () { + Navigator.maybePop(context); + }, + child: const SizedBox( + width: 240, + height: 240, + ), + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromLeft.txt b/tdesign-component/example/assets/code/popup._buildPopFromLeft.txt new file mode 100644 index 000000000..6a8544347 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromLeft.txt @@ -0,0 +1,21 @@ + + Widget _buildPopFromLeft(BuildContext context) { + return TDButton( + text: '左侧弹出', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.left, + builder: (context) { + return Container( + color: Colors.white, + width: 280, + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromRight.txt b/tdesign-component/example/assets/code/popup._buildPopFromRight.txt new file mode 100644 index 000000000..533eabc54 --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromRight.txt @@ -0,0 +1,21 @@ + + Widget _buildPopFromRight(BuildContext context) { + return TDButton( + text: '右侧弹出', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.right, + builder: (context) { + return Container( + color: Colors.white, + width: 280, + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/popup._buildPopFromTop.txt b/tdesign-component/example/assets/code/popup._buildPopFromTop.txt new file mode 100644 index 000000000..44f36671f --- /dev/null +++ b/tdesign-component/example/assets/code/popup._buildPopFromTop.txt @@ -0,0 +1,27 @@ + + Widget _buildPopFromTop(BuildContext context) { + return TDButton( + text: '顶部弹出', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.top, + open: () { + print('open'); + }, + opened: () { + print('opened'); + }, + builder: (context) { + return Container( + color: Colors.white, + height: 240, + ); + })); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildButton.txt b/tdesign-component/example/assets/code/progress._buildButton.txt new file mode 100644 index 000000000..ccedf6a25 --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildButton.txt @@ -0,0 +1,9 @@ + + Widget _buildButton(BuildContext context) { + return TDProgress( + type: TDProgressType.button, + onTap: _toggleProgress, + onLongPress: _resetProgress, + value: progressValue, + label: buttonLabel); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildCircle.txt b/tdesign-component/example/assets/code/progress._buildCircle.txt new file mode 100644 index 000000000..22c92dfcb --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildCircle.txt @@ -0,0 +1,4 @@ + + Widget _buildCircle(BuildContext context) { + return TDProgress(type: TDProgressType.circular, value: 0.3); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildCircleDanger.txt b/tdesign-component/example/assets/code/progress._buildCircleDanger.txt new file mode 100644 index 000000000..48cad74a7 --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildCircleDanger.txt @@ -0,0 +1,7 @@ + + Widget _buildCircleDanger(BuildContext context) { + return TDProgress( + type: TDProgressType.circular, + progressStatus: TDProgressStatus.danger, + value: 0.3); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildCirclePrimary.txt b/tdesign-component/example/assets/code/progress._buildCirclePrimary.txt new file mode 100644 index 000000000..d81b5ea61 --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildCirclePrimary.txt @@ -0,0 +1,7 @@ + + Widget _buildCirclePrimary(BuildContext context) { + return TDProgress( + type: TDProgressType.circular, + progressStatus: TDProgressStatus.primary, + value: 0.3); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildCircleSuccess.txt b/tdesign-component/example/assets/code/progress._buildCircleSuccess.txt new file mode 100644 index 000000000..813a2074d --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildCircleSuccess.txt @@ -0,0 +1,7 @@ + + Widget _buildCircleSuccess(BuildContext context) { + return TDProgress( + type: TDProgressType.circular, + progressStatus: TDProgressStatus.success, + value: 1); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildCircleWarning.txt b/tdesign-component/example/assets/code/progress._buildCircleWarning.txt new file mode 100644 index 000000000..eb7d2ad3c --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildCircleWarning.txt @@ -0,0 +1,7 @@ + + Widget _buildCircleWarning(BuildContext context) { + return TDProgress( + type: TDProgressType.circular, + progressStatus: TDProgressStatus.warning, + value: 0.3); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildDanger.txt b/tdesign-component/example/assets/code/progress._buildDanger.txt new file mode 100644 index 000000000..3e7359cbe --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildDanger.txt @@ -0,0 +1,9 @@ + + Widget _buildDanger(BuildContext context) { + return TDProgress( + type: TDProgressType.linear, + progressStatus: TDProgressStatus.danger, + value: 0.8, + strokeWidth: 6, + progressLabelPosition: TDProgressLabelPosition.right); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildInsideLabelLinear.txt b/tdesign-component/example/assets/code/progress._buildInsideLabelLinear.txt new file mode 100644 index 000000000..a91ab5cab --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildInsideLabelLinear.txt @@ -0,0 +1,4 @@ + + Widget _buildInsideLabelLinear(BuildContext context) { + return TDProgress(type: TDProgressType.linear, value: 0.8); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildMicro.txt b/tdesign-component/example/assets/code/progress._buildMicro.txt new file mode 100644 index 000000000..da6311ba9 --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildMicro.txt @@ -0,0 +1,4 @@ + + Widget _buildMicro(BuildContext context) { + return TDProgress(type: TDProgressType.micro, value: 0.75); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildMicroButton.txt b/tdesign-component/example/assets/code/progress._buildMicroButton.txt new file mode 100644 index 000000000..3df1ebc81 --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildMicroButton.txt @@ -0,0 +1,10 @@ + + Widget _buildMicroButton(BuildContext context) { + return TDProgress( + type: TDProgressType.micro, + value: microProgressValue, + onTap: _toggleMicroProgress, + label: TDIconLabel(isPlaying ? Icons.pause : Icons.play_arrow, + color: TDTheme.of(context).brandNormalColor), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildPrimary.txt b/tdesign-component/example/assets/code/progress._buildPrimary.txt new file mode 100644 index 000000000..86557ac70 --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildPrimary.txt @@ -0,0 +1,9 @@ + + Widget _buildPrimary(BuildContext context) { + return TDProgress( + type: TDProgressType.linear, + progressStatus: TDProgressStatus.primary, + value: 0.8, + strokeWidth: 6, + progressLabelPosition: TDProgressLabelPosition.right); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildRightLabelLinear.txt b/tdesign-component/example/assets/code/progress._buildRightLabelLinear.txt new file mode 100644 index 000000000..c20d98b64 --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildRightLabelLinear.txt @@ -0,0 +1,8 @@ + + Widget _buildRightLabelLinear(BuildContext context) { + return TDProgress( + type: TDProgressType.linear, + value: 0.8, + strokeWidth: 6, + progressLabelPosition: TDProgressLabelPosition.right); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildSuccess.txt b/tdesign-component/example/assets/code/progress._buildSuccess.txt new file mode 100644 index 000000000..9db1910c5 --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildSuccess.txt @@ -0,0 +1,9 @@ + + Widget _buildSuccess(BuildContext context) { + return TDProgress( + type: TDProgressType.linear, + progressStatus: TDProgressStatus.success, + value: 0.8, + strokeWidth: 6, + progressLabelPosition: TDProgressLabelPosition.right); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/progress._buildWarning.txt b/tdesign-component/example/assets/code/progress._buildWarning.txt new file mode 100644 index 000000000..b60e5fa82 --- /dev/null +++ b/tdesign-component/example/assets/code/progress._buildWarning.txt @@ -0,0 +1,10 @@ + + Widget _buildWarning(BuildContext context) { + return TDProgress( + type: TDProgressType.linear, + progressStatus: TDProgressStatus.warning, + value: 0.8, + strokeWidth: 6, + progressLabelPosition: TDProgressLabelPosition.right, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._checkPosition.txt b/tdesign-component/example/assets/code/radio._checkPosition.txt new file mode 100644 index 000000000..aae555570 --- /dev/null +++ b/tdesign-component/example/assets/code/radio._checkPosition.txt @@ -0,0 +1,24 @@ + + Widget _checkPosition(BuildContext context) { + return Column( + children: [ + TDRadioGroup( + contentDirection: TDContentDirection.right, + selectId: 'index:0', + child: const TDRadio( + id: 'index:0', + title: '单选', + ), + ), + TDRadioGroup( + contentDirection: TDContentDirection.left, + selectId: 'index:0', + child: const TDRadio( + id: 'index:0', + title: '单选', + showDivider: false, + ), + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._checkStyle.txt b/tdesign-component/example/assets/code/radio._checkStyle.txt new file mode 100644 index 000000000..767fd984b --- /dev/null +++ b/tdesign-component/example/assets/code/radio._checkStyle.txt @@ -0,0 +1,26 @@ + + Widget _checkStyle(BuildContext context) { + return Column( + children: [ + TDRadioGroup( + radioCheckStyle: TDRadioStyle.check, + selectId: 'index:0', + child: const TDRadio( + id: 'index:0', + title: '单选', + ), + ), + const SizedBox( + height: 17, + ), + TDRadioGroup( + radioCheckStyle: TDRadioStyle.hollowCircle, + selectId: 'index:0', + child: const TDRadio( + id: 'index:0', + title: '单选', + ), + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._customBottomLine.txt b/tdesign-component/example/assets/code/radio._customBottomLine.txt new file mode 100644 index 000000000..15ec40bde --- /dev/null +++ b/tdesign-component/example/assets/code/radio._customBottomLine.txt @@ -0,0 +1,32 @@ + + Widget _customBottomLine(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + direction: Axis.horizontal, + showDivider: true, + divider: const TDDivider( + height: 20, + color: Colors.red, + ), + directionalTdRadios: const [ + TDRadio( + id: '0', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '1', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '2', + title: '上限四字', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._customColorAndFont.txt b/tdesign-component/example/assets/code/radio._customColorAndFont.txt new file mode 100644 index 000000000..e493b65b0 --- /dev/null +++ b/tdesign-component/example/assets/code/radio._customColorAndFont.txt @@ -0,0 +1,77 @@ + + Widget _customColorAndFont(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + child: ListView( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: [ + TDRadio( + id: 'index:1', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + TDRadio( + id: 'index:2', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '单选标题多行单选标题多行单选标题多行单选标题多行单选标题多行单选标题多行', + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + TDRadio( + id: 'index:3', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息', + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + TDRadio( + id: 'index:4', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '单选标题多行单选标题多行单选标题多行单选标题多行单选标题多行单选标题多行', + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + radioStyle: TDRadioStyle.hollowCircle, + ), + TDRadio( + id: 'index:6', + title: '绿色', + titleColor: Colors.green, + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '我是蓝色并且有灰色背景', + subTitleColor: Colors.blue, + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + backgroundColor: TDTheme.of(context).grayColor2, + ), + TDRadio( + id: 'index:5', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息', + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + cardMode: true, + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._customDisableColorAndFont.txt b/tdesign-component/example/assets/code/radio._customDisableColorAndFont.txt new file mode 100644 index 000000000..b55cffe7a --- /dev/null +++ b/tdesign-component/example/assets/code/radio._customDisableColorAndFont.txt @@ -0,0 +1,30 @@ + + Widget _customDisableColorAndFont(BuildContext context) { + return TDRadioGroup( + contentDirection: TDContentDirection.right, + selectId: '0', + child: Column( + children: [ + TDRadio( + id: '0', + title: '选项禁用-已选', + subTitle: '描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息', + radioStyle: TDRadioStyle.circle, + enable: false, + disableColor: TDTheme.of(context).errorColor1, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + TDRadio( + id: '1', + title: '选项禁用-默认', + radioStyle: TDRadioStyle.circle, + enable: false, + disableColor: TDTheme.of(context).errorColor1, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._horizontalCardStyle.txt b/tdesign-component/example/assets/code/radio._horizontalCardStyle.txt new file mode 100644 index 000000000..6f09ced88 --- /dev/null +++ b/tdesign-component/example/assets/code/radio._horizontalCardStyle.txt @@ -0,0 +1,31 @@ + + Widget _horizontalCardStyle(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + cardMode: true, + direction: Axis.horizontal, + rowCount: 2, + directionalTdRadios: const [ + TDRadio( + id: 'index:0', + title: '单选', + cardMode: true, + ), + TDRadio( + id: 'index:1', + title: '单选', + cardMode: true, + ), + TDRadio( + id: 'index:2', + title: '单选', + cardMode: true, + ), + TDRadio( + id: 'index:3', + title: '单选', + cardMode: true, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._horizontalRadios.txt b/tdesign-component/example/assets/code/radio._horizontalRadios.txt new file mode 100644 index 000000000..4814f55ab --- /dev/null +++ b/tdesign-component/example/assets/code/radio._horizontalRadios.txt @@ -0,0 +1,27 @@ + + Widget _horizontalRadios(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + direction: Axis.horizontal, + directionalTdRadios: const [ + TDRadio( + id: '0', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '1', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '2', + title: '上限四字', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._passThroughStyle.txt b/tdesign-component/example/assets/code/radio._passThroughStyle.txt new file mode 100644 index 000000000..8d32de2aa --- /dev/null +++ b/tdesign-component/example/assets/code/radio._passThroughStyle.txt @@ -0,0 +1,21 @@ + + Widget _passThroughStyle(BuildContext context) { + return TDRadioGroup( + selectId: 'index:0', + passThrough: true, + child: ListView.builder( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + var title = '单选'; + return TDRadio( + id: 'index:$index', + title: title, + size: TDCheckBoxSize.large, + ); + }, + itemCount: 4, + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._radioStatus.txt b/tdesign-component/example/assets/code/radio._radioStatus.txt new file mode 100644 index 000000000..993444388 --- /dev/null +++ b/tdesign-component/example/assets/code/radio._radioStatus.txt @@ -0,0 +1,23 @@ + + Widget _radioStatus(BuildContext context) { + return TDRadioGroup( + contentDirection: TDContentDirection.right, + selectId: '0', + child: const Column( + children: [ + TDRadio( + id: '0', + title: '选项禁用-已选', + radioStyle: TDRadioStyle.circle, + enable: false, + ), + TDRadio( + id: '1', + title: '选项禁用-默认', + radioStyle: TDRadioStyle.circle, + enable: false, + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._showBottomLine.txt b/tdesign-component/example/assets/code/radio._showBottomLine.txt new file mode 100644 index 000000000..a14681fdb --- /dev/null +++ b/tdesign-component/example/assets/code/radio._showBottomLine.txt @@ -0,0 +1,28 @@ + + Widget _showBottomLine(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + direction: Axis.horizontal, + showDivider: true, + directionalTdRadios: const [ + TDRadio( + id: '0', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '1', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '2', + title: '上限四字', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._verticalCardStyle.txt b/tdesign-component/example/assets/code/radio._verticalCardStyle.txt new file mode 100644 index 000000000..3794ac0f6 --- /dev/null +++ b/tdesign-component/example/assets/code/radio._verticalCardStyle.txt @@ -0,0 +1,42 @@ + + Widget _verticalCardStyle(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + cardMode: true, + direction: Axis.vertical, + directionalTdRadios: const [ + TDRadio( + id: 'index:0', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDRadio( + id: 'index:1', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDRadio( + id: 'index:2', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDRadio( + id: 'index:3', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radio._verticalRadios.txt b/tdesign-component/example/assets/code/radio._verticalRadios.txt new file mode 100644 index 000000000..dda875363 --- /dev/null +++ b/tdesign-component/example/assets/code/radio._verticalRadios.txt @@ -0,0 +1,23 @@ + + Widget _verticalRadios(BuildContext context) { + return TDCell( + title: '单选标题', + hover: false, + required: true, + descriptionWidget: TDRadioGroup( + selectId: '0', + direction: Axis.horizontal, + directionalTdRadios: const [TDRadio( + id: '0', + title: '单选标题0', + showDivider: false, + ), + TDRadio( + id: '1', + title: '单选标题1', + showDivider: false, + ), + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radius._buildRadiusCircle.txt b/tdesign-component/example/assets/code/radius._buildRadiusCircle.txt new file mode 100644 index 000000000..ca57b90c3 --- /dev/null +++ b/tdesign-component/example/assets/code/radius._buildRadiusCircle.txt @@ -0,0 +1,12 @@ + + Widget _buildRadiusCircle(BuildContext context) { + // 圆形与胶囊型一致,如果长款一致即是圆形 + return Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusCircle)), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radius._buildRadiusDefault.txt b/tdesign-component/example/assets/code/radius._buildRadiusDefault.txt new file mode 100644 index 000000000..acd5e2bda --- /dev/null +++ b/tdesign-component/example/assets/code/radius._buildRadiusDefault.txt @@ -0,0 +1,11 @@ + + Widget _buildRadiusDefault(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radius._buildRadiusExtraLarge.txt b/tdesign-component/example/assets/code/radius._buildRadiusExtraLarge.txt new file mode 100644 index 000000000..1a33c67e9 --- /dev/null +++ b/tdesign-component/example/assets/code/radius._buildRadiusExtraLarge.txt @@ -0,0 +1,11 @@ + + Widget _buildRadiusExtraLarge(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusExtraLarge)), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radius._buildRadiusLarge.txt b/tdesign-component/example/assets/code/radius._buildRadiusLarge.txt new file mode 100644 index 000000000..d52e5cf28 --- /dev/null +++ b/tdesign-component/example/assets/code/radius._buildRadiusLarge.txt @@ -0,0 +1,10 @@ + + Widget _buildRadiusLarge(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radius._buildRadiusRound.txt b/tdesign-component/example/assets/code/radius._buildRadiusRound.txt new file mode 100644 index 000000000..f7ea005e3 --- /dev/null +++ b/tdesign-component/example/assets/code/radius._buildRadiusRound.txt @@ -0,0 +1,11 @@ + + Widget _buildRadiusRound(BuildContext context) { + // 胶囊型,数值设置较大 + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusRound)), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/radius._buildRadiusSmall.txt b/tdesign-component/example/assets/code/radius._buildRadiusSmall.txt new file mode 100644 index 000000000..a82ce5072 --- /dev/null +++ b/tdesign-component/example/assets/code/radius._buildRadiusSmall.txt @@ -0,0 +1,10 @@ + + Widget _buildRadiusSmall(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusSmall)), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/rate._buildColorRate.txt b/tdesign-component/example/assets/code/rate._buildColorRate.txt new file mode 100644 index 000000000..9b7763be5 --- /dev/null +++ b/tdesign-component/example/assets/code/rate._buildColorRate.txt @@ -0,0 +1,14 @@ + + Widget _buildColorRate(BuildContext context) { + return Column(children: const [ + TDCell( + title: '填充评分', + noteWidget: TDRate( + value: 2.5, + allowHalf: true, + color: [Color(0xFFFFC51C), Color(0xFFE8E8E8)], + )), + SizedBox(height: 16), + TDCell(title: '线描评分', noteWidget: TDRate(value: 2.5, allowHalf: true, color: [Color(0xFF00A870)])), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/rate._buildCusRate.txt b/tdesign-component/example/assets/code/rate._buildCusRate.txt new file mode 100644 index 000000000..4d8f1bea8 --- /dev/null +++ b/tdesign-component/example/assets/code/rate._buildCusRate.txt @@ -0,0 +1,4 @@ + + Widget _buildCusRate(BuildContext context) { + return const TDCell(title: '自定义评分', noteWidget: TDRate(value: 3, icon: [TDIcons.thumb_up])); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/rate._buildDRate.txt b/tdesign-component/example/assets/code/rate._buildDRate.txt new file mode 100644 index 000000000..10d479cfe --- /dev/null +++ b/tdesign-component/example/assets/code/rate._buildDRate.txt @@ -0,0 +1,10 @@ + + Widget _buildDRate(BuildContext context) { + return Column(children: const [ + TDCell(title: '顶部显示', noteWidget: TDRate(placement: PlacementEnum.top)), + SizedBox(height: 16), + TDCell(title: '不显示', noteWidget: TDRate(placement: PlacementEnum.none)), + SizedBox(height: 16), + TDCell(title: '底部显示', noteWidget: TDRate(placement: PlacementEnum.bottom)), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/rate._buildFilledRate.txt b/tdesign-component/example/assets/code/rate._buildFilledRate.txt new file mode 100644 index 000000000..aef3d6f74 --- /dev/null +++ b/tdesign-component/example/assets/code/rate._buildFilledRate.txt @@ -0,0 +1,4 @@ + + Widget _buildFilledRate(BuildContext context) { + return const TDCell(title: '实心评分', noteWidget: TDRate(value: 3)); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/rate._buildFullRate.txt b/tdesign-component/example/assets/code/rate._buildFullRate.txt new file mode 100644 index 000000000..52da296e3 --- /dev/null +++ b/tdesign-component/example/assets/code/rate._buildFullRate.txt @@ -0,0 +1,4 @@ + + Widget _buildFullRate(BuildContext context) { + return const TDCell(title: '点击活滑动', noteWidget: TDRate(value: 3)); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/rate._buildHalfRate.txt b/tdesign-component/example/assets/code/rate._buildHalfRate.txt new file mode 100644 index 000000000..bf7eff272 --- /dev/null +++ b/tdesign-component/example/assets/code/rate._buildHalfRate.txt @@ -0,0 +1,4 @@ + + Widget _buildHalfRate(BuildContext context) { + return const TDCell(title: '点击活滑动', noteWidget: TDRate(value: 3, allowHalf: true, onChange: print,)); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/rate._buildMsgRate.txt b/tdesign-component/example/assets/code/rate._buildMsgRate.txt new file mode 100644 index 000000000..b95761407 --- /dev/null +++ b/tdesign-component/example/assets/code/rate._buildMsgRate.txt @@ -0,0 +1,8 @@ + + Widget _buildMsgRate(BuildContext context) { + return Column(children: const [ + TDCell(title: '带描述评分', noteWidget: TDRate(value: 3, showText: true, texts: ['1分', '2分', '3分', '4分', '5分'])), + SizedBox(height: 16), + TDCell(title: '带描述评分', noteWidget: TDRate(value: 3, showText: true)) + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/rate._buildNumRate.txt b/tdesign-component/example/assets/code/rate._buildNumRate.txt new file mode 100644 index 000000000..ff120d913 --- /dev/null +++ b/tdesign-component/example/assets/code/rate._buildNumRate.txt @@ -0,0 +1,9 @@ + + Widget _buildNumRate(BuildContext context) { + return const TDCell( + title: '自定义评分数量', + noteWidget: TDRate( + value: 2, + count: 3, + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/rate._buildOtherRate.txt b/tdesign-component/example/assets/code/rate._buildOtherRate.txt new file mode 100644 index 000000000..4744c6da5 --- /dev/null +++ b/tdesign-component/example/assets/code/rate._buildOtherRate.txt @@ -0,0 +1,33 @@ + + Widget _buildOtherRate(BuildContext context) { + var texts = ['非常糟糕', '有些糟糕', '可以尝试', '可以前往', '推荐前往']; + return Container( + width: double.infinity, + child: Center( + child: TDRate( + value: 2, + size: 30, + showText: true, + // texts: ['非常糟糕', '有些糟糕', '可以尝试', '可以前往', '推荐前往'], + direction: Axis.vertical, + // mainAxisAlignment: MainAxisAlignment.center, + // textWidth: 64, + builderText: (context, value) { + return value == 0 + ? const SizedBox.shrink() + : Padding( + padding: EdgeInsets.only(top: TDTheme.of(context).spacer8), + child: TDText( + texts[(value - 1).toInt()], + font: TDTheme.of(context).fontTitleMedium, + textColor: TDTheme.of(context).warningColor5, + ), + ); + }, + ), + ), + + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + color: Colors.white, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/rate._buildSizeRate.txt b/tdesign-component/example/assets/code/rate._buildSizeRate.txt new file mode 100644 index 000000000..39cf44b76 --- /dev/null +++ b/tdesign-component/example/assets/code/rate._buildSizeRate.txt @@ -0,0 +1,8 @@ + + Widget _buildSizeRate(BuildContext context) { + return Column(children: const [ + TDCell(title: '默认尺寸24', noteWidget: TDRate(value: 3)), + SizedBox(height: 16), + TDCell(title: '小尺寸20', noteWidget: TDRate(value: 3, size: 20)), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/refresh._buildRefresh.txt b/tdesign-component/example/assets/code/refresh._buildRefresh.txt new file mode 100644 index 000000000..7b6196cef --- /dev/null +++ b/tdesign-component/example/assets/code/refresh._buildRefresh.txt @@ -0,0 +1,48 @@ + + Widget _buildRefresh(BuildContext context) { + return EasyRefresh( + // 下拉样式 + header: TDRefreshHeader(), + child: SingleChildScrollView( + child: Column( + children: [ + Container( + height: 171, + alignment: Alignment.center, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.all(Radius.circular(TDTheme.of(context).radiusLarge))), + margin: const EdgeInsets.only(left: 16, right: 16), + child: TDText( + PlatformUtil.isWeb ? 'Web暂不支持下拉,请下载安装apk体验' : '拖拽该区域演示 顶部下拉刷新', + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).fontGyColor4, + ), + ), + Container( + height: 70, + alignment: Alignment.center, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.all(Radius.circular(TDTheme.of(context).radiusLarge))), + margin: const EdgeInsets.only(top: 16, left: 16, right: 16), + child: TDText( + '下拉刷新次数:${count}', + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).fontGyColor4, + ), + ), + const SizedBox(height: 500), + ], + ), + ), + // 下拉刷新回调 + onRefresh: () { + Future.delayed(const Duration(seconds: 2), () { + setState(() { + count++; + }); + }); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/result._buildBasicResultDefault.txt b/tdesign-component/example/assets/code/result._buildBasicResultDefault.txt new file mode 100644 index 000000000..02aca64c2 --- /dev/null +++ b/tdesign-component/example/assets/code/result._buildBasicResultDefault.txt @@ -0,0 +1,7 @@ + + TDResult _buildBasicResultDefault(BuildContext context) { + return const TDResult( + title: '默认状态', + theme: TDResultTheme.defaultTheme, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/result._buildBasicResultError.txt b/tdesign-component/example/assets/code/result._buildBasicResultError.txt new file mode 100644 index 000000000..2b657efef --- /dev/null +++ b/tdesign-component/example/assets/code/result._buildBasicResultError.txt @@ -0,0 +1,7 @@ + + TDResult _buildBasicResultError(BuildContext context) { + return const TDResult( + title: '失败状态', + theme: TDResultTheme.error, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/result._buildBasicResultSuccess.txt b/tdesign-component/example/assets/code/result._buildBasicResultSuccess.txt new file mode 100644 index 000000000..c5d82281e --- /dev/null +++ b/tdesign-component/example/assets/code/result._buildBasicResultSuccess.txt @@ -0,0 +1,7 @@ + + TDResult _buildBasicResultSuccess(BuildContext context) { + return const TDResult( + title: '成功状态', + theme: TDResultTheme.success, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/result._buildBasicResultWarning.txt b/tdesign-component/example/assets/code/result._buildBasicResultWarning.txt new file mode 100644 index 000000000..29d30c0d9 --- /dev/null +++ b/tdesign-component/example/assets/code/result._buildBasicResultWarning.txt @@ -0,0 +1,7 @@ + + TDResult _buildBasicResultWarning(BuildContext context) { + return const TDResult( + title: '警示状态', + theme: TDResultTheme.warning, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/result._buildCustomResultContent.txt b/tdesign-component/example/assets/code/result._buildCustomResultContent.txt new file mode 100644 index 000000000..ae24433bd --- /dev/null +++ b/tdesign-component/example/assets/code/result._buildCustomResultContent.txt @@ -0,0 +1,8 @@ + + TDResult _buildCustomResultContent(BuildContext context) { + return TDResult( + title: '自定义结果', + icon: Image.asset('assets/img/illustration.png'), + description: '描述文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/result._buildResultWithDescriptionDefault.txt b/tdesign-component/example/assets/code/result._buildResultWithDescriptionDefault.txt new file mode 100644 index 000000000..a3dea4fa6 --- /dev/null +++ b/tdesign-component/example/assets/code/result._buildResultWithDescriptionDefault.txt @@ -0,0 +1,8 @@ + + TDResult _buildResultWithDescriptionDefault(BuildContext context) { + return const TDResult( + title: '默认状态', + theme: TDResultTheme.defaultTheme, + description: '描述文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/result._buildResultWithDescriptionError.txt b/tdesign-component/example/assets/code/result._buildResultWithDescriptionError.txt new file mode 100644 index 000000000..d690ab258 --- /dev/null +++ b/tdesign-component/example/assets/code/result._buildResultWithDescriptionError.txt @@ -0,0 +1,8 @@ + + TDResult _buildResultWithDescriptionError(BuildContext context) { + return const TDResult( + title: '失败状态', + theme: TDResultTheme.error, + description: '描述文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/result._buildResultWithDescriptionSuccess.txt b/tdesign-component/example/assets/code/result._buildResultWithDescriptionSuccess.txt new file mode 100644 index 000000000..2405d702b --- /dev/null +++ b/tdesign-component/example/assets/code/result._buildResultWithDescriptionSuccess.txt @@ -0,0 +1,8 @@ + + TDResult _buildResultWithDescriptionSuccess(BuildContext context) { + return const TDResult( + title: '成功状态', + theme: TDResultTheme.success, + description: '描述文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/result._buildResultWithDescriptionWarning.txt b/tdesign-component/example/assets/code/result._buildResultWithDescriptionWarning.txt new file mode 100644 index 000000000..a5ef713b5 --- /dev/null +++ b/tdesign-component/example/assets/code/result._buildResultWithDescriptionWarning.txt @@ -0,0 +1,8 @@ + + TDResult _buildResultWithDescriptionWarning(BuildContext context) { + return const TDResult( + title: '警示状态', + theme: TDResultTheme.warning, + description: '描述文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/search._buildCenterSearchBar.txt b/tdesign-component/example/assets/code/search._buildCenterSearchBar.txt new file mode 100644 index 000000000..b60befa70 --- /dev/null +++ b/tdesign-component/example/assets/code/search._buildCenterSearchBar.txt @@ -0,0 +1,12 @@ + + Widget _buildCenterSearchBar(BuildContext context) { + return TDSearchBar( + placeHolder: '搜索预设文案', + alignment: TDSearchAlignment.center, + onTextChanged: (String text) { + setState(() { + inputText = text; + }); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/search._buildColumnWidgets.txt b/tdesign-component/example/assets/code/search._buildColumnWidgets.txt new file mode 100644 index 000000000..7b57eba5f --- /dev/null +++ b/tdesign-component/example/assets/code/search._buildColumnWidgets.txt @@ -0,0 +1,11 @@ + + Widget _buildColumnWidgets(BuildContext context, Widget widget) { + return Column( + children: [ + widget, + const SizedBox( + height: 16, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/search._buildDefaultSearchBar.txt b/tdesign-component/example/assets/code/search._buildDefaultSearchBar.txt new file mode 100644 index 000000000..642189a27 --- /dev/null +++ b/tdesign-component/example/assets/code/search._buildDefaultSearchBar.txt @@ -0,0 +1,13 @@ + + Widget _buildDefaultSearchBar(BuildContext context) { + return _buildColumnWidgets( + context, + TDSearchBar( + placeHolder: '搜索预设文案', + onTextChanged: (String text) { + setState(() { + inputText = text; + }); + }, + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/search._buildFocusSearchBar.txt b/tdesign-component/example/assets/code/search._buildFocusSearchBar.txt new file mode 100644 index 000000000..162d47bb1 --- /dev/null +++ b/tdesign-component/example/assets/code/search._buildFocusSearchBar.txt @@ -0,0 +1,8 @@ + + Widget _buildFocusSearchBar(BuildContext context) { + return const TDSearchBar( + placeHolder: '搜索预设文案', + needCancel: true, + autoFocus: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/search._buildFocusSearchBarWithAction.txt b/tdesign-component/example/assets/code/search._buildFocusSearchBarWithAction.txt new file mode 100644 index 000000000..ef73cfc5f --- /dev/null +++ b/tdesign-component/example/assets/code/search._buildFocusSearchBarWithAction.txt @@ -0,0 +1,22 @@ + + Widget _buildFocusSearchBarWithAction(BuildContext context) { + return TDSearchBar( + placeHolder: '搜索预设文案', + action: '搜索', + needCancel: true, + controller: inputController, + onActionClick: (value) { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + content: inputController.text.isNotEmpty + ? '搜索关键词:${inputController.text}' + : '搜索关键词为空', + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/search._buildSearchBarWithAction.txt b/tdesign-component/example/assets/code/search._buildSearchBarWithAction.txt new file mode 100644 index 000000000..cf0e96e91 --- /dev/null +++ b/tdesign-component/example/assets/code/search._buildSearchBarWithAction.txt @@ -0,0 +1,32 @@ + + Widget _buildSearchBarWithAction(BuildContext context) { + return Column( + children: [ + TDSearchBar( + placeHolder: '搜索预设文案', + alignment: TDSearchAlignment.left, + action: '搜索', + onActionClick: (String text) { + setState(() { + searchText = text; + }); + }, + onTextChanged: (String text) { + setState(() { + inputText = text; + }); + }, + ), + const SizedBox( + height: 10, + ), + Container( + padding: const EdgeInsets.only(left: 15), + alignment: Alignment.centerLeft, + child: TDText( + '搜索框输入的内容:${searchText ?? ''}', + ), + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/search._buildSearchBarWithShape.txt b/tdesign-component/example/assets/code/search._buildSearchBarWithShape.txt new file mode 100644 index 000000000..03cf1140a --- /dev/null +++ b/tdesign-component/example/assets/code/search._buildSearchBarWithShape.txt @@ -0,0 +1,31 @@ + + Widget _buildSearchBarWithShape(BuildContext context) { + return Column( + children: [ + _buildColumnWidgets( + context, + TDSearchBar( + placeHolder: '搜索预设文案', + style: TDSearchStyle.square, + onTextChanged: (String text) { + setState(() { + inputText = text; + }); + }, + ), + ), + _buildColumnWidgets( + context, + TDSearchBar( + placeHolder: '搜索预设文案', + style: TDSearchStyle.round, + onTextChanged: (String text) { + setState(() { + inputText = text; + }); + }, + ), + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/shadows._buildShadowsBase.txt b/tdesign-component/example/assets/code/shadows._buildShadowsBase.txt new file mode 100644 index 000000000..0bd50f8a5 --- /dev/null +++ b/tdesign-component/example/assets/code/shadows._buildShadowsBase.txt @@ -0,0 +1,12 @@ + + Widget _buildShadowsBase(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + boxShadow: TDTheme.of(context).shadowsBase, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/shadows._buildShadowsMiddle.txt b/tdesign-component/example/assets/code/shadows._buildShadowsMiddle.txt new file mode 100644 index 000000000..7a8e19898 --- /dev/null +++ b/tdesign-component/example/assets/code/shadows._buildShadowsMiddle.txt @@ -0,0 +1,12 @@ + + Widget _buildShadowsMiddle(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + boxShadow: TDTheme.of(context).shadowsMiddle, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/shadows._buildShadowsTop.txt b/tdesign-component/example/assets/code/shadows._buildShadowsTop.txt new file mode 100644 index 000000000..19a05653d --- /dev/null +++ b/tdesign-component/example/assets/code/shadows._buildShadowsTop.txt @@ -0,0 +1,12 @@ + + Widget _buildShadowsTop(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + boxShadow: TDTheme.of(context).shadowsTop, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/sideBar._buildAnchorSideBar.txt b/tdesign-component/example/assets/code/sideBar._buildAnchorSideBar.txt new file mode 100644 index 000000000..050c705f2 --- /dev/null +++ b/tdesign-component/example/assets/code/sideBar._buildAnchorSideBar.txt @@ -0,0 +1,62 @@ + + Widget _buildAnchorSideBar(BuildContext context) { + // 锚点用法 + final list = []; + final pages = []; + + for (var i = 0; i < 20; i++) { + list.add(SideItemProps( + index: i, + label: '选项', + value: i, + )); + pages.add(getAnchorDemo(i)); + } + + pages.add(Container( + height: MediaQuery.of(context).size.height - itemHeight, + decoration: const BoxDecoration(color: Colors.white), + )); + + list[1].badge = const TDBadge(TDBadgeType.redPoint); + list[2].badge = const TDBadge( + TDBadgeType.message, + count: '8', + ); + + var demoHeight = MediaQuery.of(context).size.height; + _sideBarController.init(list); + + return Row( + children: [ + SizedBox( + width: 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.normal, + value: currentValue, + controller: _sideBarController, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + icon: ele.icon)) + .toList(), + onChanged: onChanged, + onSelected: onSelected, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: SingleChildScrollView( + controller: _demoScroller, + child: Column( + children: pages, + ), + ), + )) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/sideBar._buildCustomSideBar.txt b/tdesign-component/example/assets/code/sideBar._buildCustomSideBar.txt new file mode 100644 index 000000000..cf7ec593c --- /dev/null +++ b/tdesign-component/example/assets/code/sideBar._buildCustomSideBar.txt @@ -0,0 +1,68 @@ + + Widget _buildCustomSideBar(BuildContext context) { + // 自定义样式 + final list = []; + final pages = []; + + for (var i = 0; i < 100; i++) { + list.add(SideItemProps( + index: i, + label: '选项', + value: i, + )); + pages.add(getPageDemo(i)); + } + + list[1].badge = const TDBadge(TDBadgeType.redPoint); + list[2].badge = const TDBadge( + TDBadgeType.message, + count: '8', + ); + list[1].textStyle = const TextStyle(color: Colors.green); + + void setCurrentValue(int value) { + _pageController.jumpToPage(value); + if (currentValue != value) { + currentValue = value; + } + } + + var demoHeight = MediaQuery.of(context).size.height; + + return Row( + children: [ + SizedBox( + width: 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.normal, + value: currentValue, + controller: _sideBarController, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + textStyle: ele.textStyle, + icon: ele.icon)) + .toList(), + selectedTextStyle:TextStyle(color: Colors.red), + onSelected: setCurrentValue, + contentPadding:EdgeInsets.only(left: 16, top: 16,bottom: 16), + selectedBgColor: Colors.blue, + unSelectedBgColor: Colors.yellow, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: PageView( + controller: _pageController, + scrollDirection: Axis.vertical, + children: pages, + physics: const NeverScrollableScrollPhysics(), + ), + )) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/sideBar._buildIconSideBar.txt b/tdesign-component/example/assets/code/sideBar._buildIconSideBar.txt new file mode 100644 index 000000000..c2080f8ea --- /dev/null +++ b/tdesign-component/example/assets/code/sideBar._buildIconSideBar.txt @@ -0,0 +1,57 @@ + + Widget _buildIconSideBar(BuildContext context) { + final list = []; + final pages = []; + + for (var i = 0; i < 20; i++) { + list.add( + SideItemProps(index: i, label: '选项', value: i, icon: TDIcons.app)); + pages.add(getAnchorDemo(i)); + } + + pages.add(Container( + height: MediaQuery.of(context).size.height - itemHeight, + decoration: const BoxDecoration(color: Colors.white), + )); + + list[1].badge = const TDBadge(TDBadgeType.redPoint); + list[2].badge = const TDBadge( + TDBadgeType.message, + count: '8', + ); + + var demoHeight = MediaQuery.of(context).size.height; + + return Row( + children: [ + SizedBox( + width: 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.normal, + value: currentValue, + controller: _sideBarController, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + icon: ele.icon)) + .toList(), + onChanged: onChanged, + onSelected: onSelected, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: SingleChildScrollView( + controller: _demoScroller, + child: Column( + children: pages, + ), + ), + )) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/sideBar._buildLoadingSideBar.txt b/tdesign-component/example/assets/code/sideBar._buildLoadingSideBar.txt new file mode 100644 index 000000000..a137c9589 --- /dev/null +++ b/tdesign-component/example/assets/code/sideBar._buildLoadingSideBar.txt @@ -0,0 +1,41 @@ + + Widget _buildLoadingSideBar(BuildContext context) { + // 延迟加载 + Future.delayed(const Duration(seconds: 3), _initData); + var size = MediaQuery.of(context).size; + var demoHeight = size.height; + + return Row( + children: [ + SizedBox( + width: list.isEmpty ? size.width : 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.normal, + value: currentValue, + controller: _sideBarController, + loading: true, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + icon: ele.icon)) + .toList(), + onChanged: onChanged, + onSelected: onSelected, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: SingleChildScrollView( + controller: _demoScroller, + child: Column( + children: pages, + ), + ), + )) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/sideBar._buildOutlineSideBar.txt b/tdesign-component/example/assets/code/sideBar._buildOutlineSideBar.txt new file mode 100644 index 000000000..9eaf27228 --- /dev/null +++ b/tdesign-component/example/assets/code/sideBar._buildOutlineSideBar.txt @@ -0,0 +1,61 @@ + + Widget _buildOutlineSideBar(BuildContext context) { + // 非通栏选项样式 + final list = []; + final pages = []; + + for (var i = 0; i < 20; i++) { + list.add(SideItemProps( + index: i, + label: '选项', + value: i, + )); + pages.add(getAnchorDemo(i)); + } + + pages.add(Container( + height: MediaQuery.of(context).size.height - itemHeight, + decoration: const BoxDecoration(color: Colors.white), + )); + + list[1].badge = const TDBadge(TDBadgeType.redPoint); + list[2].badge = const TDBadge( + TDBadgeType.message, + count: '8', + ); + + var demoHeight = MediaQuery.of(context).size.height; + + return Row( + children: [ + SizedBox( + width: 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.outline, + value: currentValue, + controller: _sideBarController, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + icon: ele.icon)) + .toList(), + onChanged: onChanged, + onSelected: onSelected, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: SingleChildScrollView( + controller: _demoScroller, + child: Column( + children: pages, + ), + ), + )) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/sideBar._buildPaginationSideBar.txt b/tdesign-component/example/assets/code/sideBar._buildPaginationSideBar.txt new file mode 100644 index 000000000..47bcb6fdd --- /dev/null +++ b/tdesign-component/example/assets/code/sideBar._buildPaginationSideBar.txt @@ -0,0 +1,62 @@ + + Widget _buildPaginationSideBar(BuildContext context) { + // 切页用法 + final list = []; + final pages = []; + + for (var i = 0; i < 100; i++) { + list.add(SideItemProps( + index: i, + label: '选项', + value: i, + )); + pages.add(getPageDemo(i)); + } + + list[1].badge = const TDBadge(TDBadgeType.redPoint); + list[2].badge = const TDBadge( + TDBadgeType.message, + count: '8', + ); + + void setCurrentValue(int value) { + _pageController.jumpToPage(value); + if (currentValue != value) { + currentValue = value; + } + } + + var demoHeight = MediaQuery.of(context).size.height; + + return Row( + children: [ + SizedBox( + width: 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.normal, + value: currentValue, + controller: _sideBarController, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + icon: ele.icon)) + .toList(), + onSelected: setCurrentValue, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: PageView( + controller: _pageController, + scrollDirection: Axis.vertical, + children: pages, + physics: const NeverScrollableScrollPhysics(), + ), + )) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/skeleton._buildAvatarSkeleton.txt b/tdesign-component/example/assets/code/skeleton._buildAvatarSkeleton.txt new file mode 100644 index 000000000..dc991d553 --- /dev/null +++ b/tdesign-component/example/assets/code/skeleton._buildAvatarSkeleton.txt @@ -0,0 +1,4 @@ + + Widget _buildAvatarSkeleton(BuildContext context) { + return TDSkeleton(theme: TDSkeletonTheme.avatar); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/skeleton._buildCellSkeleton.txt b/tdesign-component/example/assets/code/skeleton._buildCellSkeleton.txt new file mode 100644 index 000000000..aa53721e8 --- /dev/null +++ b/tdesign-component/example/assets/code/skeleton._buildCellSkeleton.txt @@ -0,0 +1,37 @@ + + Widget _buildCellSkeleton(BuildContext context) { + var rowColsAvatar = TDSkeleton(theme: TDSkeletonTheme.avatar); + var rowColsImage = TDSkeleton.fromRowCol( + rowCol: TDSkeletonRowCol(objects: const [ + [TDSkeletonRowColObj.rect(width: 48, height: 48, flex: null)] + ]), + ); + var rowColsContent = TDSkeleton.fromRowCol( + rowCol: TDSkeletonRowCol( + objects: const [ + [TDSkeletonRowColObj(), TDSkeletonRowColObj.spacer(flex: 1)], + [TDSkeletonRowColObj()] + ], + ), + ); + + return Column( + children: [ + Row( + children: [ + rowColsAvatar, + const SizedBox(width: 12), + rowColsContent, + ], + ), + const SizedBox(height: 16), + Row( + children: [ + rowColsImage, + const SizedBox(width: 12), + rowColsContent, + ], + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/skeleton._buildCombineSkeleton.txt b/tdesign-component/example/assets/code/skeleton._buildCombineSkeleton.txt new file mode 100644 index 000000000..825aa5e1c --- /dev/null +++ b/tdesign-component/example/assets/code/skeleton._buildCombineSkeleton.txt @@ -0,0 +1,37 @@ + + Widget _buildCombineSkeleton(BuildContext context) { + var rowCols = Flexible( + child: LayoutBuilder( + builder: (context, constraints) => Row(children: [ + TDSkeleton.fromRowCol( + rowCol: TDSkeletonRowCol( + objects: [ + [ + TDSkeletonRowColObj( + width: constraints.maxWidth*0.96, + height: constraints.maxWidth, + flex: null, + style: TDSkeletonRowColObjStyle( + borderRadius: (context) => + TDTheme.of(context).radiusExtraLarge)) + ], + [TDSkeletonRowColObj.text( + width: constraints.maxWidth*0.96, + )], + const [ + TDSkeletonRowColObj.text(), + TDSkeletonRowColObj.spacer(flex: 1), + ], + ], + ), + ) + ]))); + + return Row( + children: [ + rowCols, + SizedBox(width: TDTheme.of(context).spacer4), + rowCols, + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/skeleton._buildFlashedSkeleton.txt b/tdesign-component/example/assets/code/skeleton._buildFlashedSkeleton.txt new file mode 100644 index 000000000..828fe634f --- /dev/null +++ b/tdesign-component/example/assets/code/skeleton._buildFlashedSkeleton.txt @@ -0,0 +1,7 @@ + + Widget _buildFlashedSkeleton(BuildContext context) { + return TDSkeleton( + animation: TDSkeletonAnimation.flashed, + theme: TDSkeletonTheme.paragraph, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/skeleton._buildGradientSkeleton.txt b/tdesign-component/example/assets/code/skeleton._buildGradientSkeleton.txt new file mode 100644 index 000000000..53c3027f4 --- /dev/null +++ b/tdesign-component/example/assets/code/skeleton._buildGradientSkeleton.txt @@ -0,0 +1,7 @@ + + Widget _buildGradientSkeleton(BuildContext context) { + return TDSkeleton( + animation: TDSkeletonAnimation.gradient, + theme: TDSkeletonTheme.paragraph, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/skeleton._buildGridSkeleton.txt b/tdesign-component/example/assets/code/skeleton._buildGridSkeleton.txt new file mode 100644 index 000000000..5dc15b994 --- /dev/null +++ b/tdesign-component/example/assets/code/skeleton._buildGridSkeleton.txt @@ -0,0 +1,15 @@ + + Widget _buildGridSkeleton(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + for (var i = 0; i < 5; i++) + TDSkeleton.fromRowCol( + rowCol: TDSkeletonRowCol(objects: const [ + [TDSkeletonRowColObj.rect(width: 48, height: 48, flex: null)], + [TDSkeletonRowColObj.text(width: 48, flex: null)], + ]), + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/skeleton._buildImageSkeleton.txt b/tdesign-component/example/assets/code/skeleton._buildImageSkeleton.txt new file mode 100644 index 000000000..a3fa9dd8e --- /dev/null +++ b/tdesign-component/example/assets/code/skeleton._buildImageSkeleton.txt @@ -0,0 +1,4 @@ + + Widget _buildImageSkeleton(BuildContext context) { + return TDSkeleton(theme: TDSkeletonTheme.image); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/skeleton._buildParagraphSkeleton.txt b/tdesign-component/example/assets/code/skeleton._buildParagraphSkeleton.txt new file mode 100644 index 000000000..9e614b210 --- /dev/null +++ b/tdesign-component/example/assets/code/skeleton._buildParagraphSkeleton.txt @@ -0,0 +1,4 @@ + + Widget _buildParagraphSkeleton(BuildContext context) { + return TDSkeleton(theme: TDSkeletonTheme.paragraph); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/skeleton._buildTextSkeleton.txt b/tdesign-component/example/assets/code/skeleton._buildTextSkeleton.txt new file mode 100644 index 000000000..4aea5741b --- /dev/null +++ b/tdesign-component/example/assets/code/skeleton._buildTextSkeleton.txt @@ -0,0 +1,4 @@ + + Widget _buildTextSkeleton(BuildContext context) { + return TDSkeleton(theme: TDSkeletonTheme.text); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildCapsule.txt b/tdesign-component/example/assets/code/slider._buildCapsule.txt new file mode 100644 index 000000000..71dbd326a --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildCapsule.txt @@ -0,0 +1,104 @@ + + Widget _buildCapsule(BuildContext context) { + return Column( + children: [ + TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showThumbValue: true, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: 40, + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + leftLabel: '0', + rightLabel: '100', + value: 40, + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + showThumbValue: true, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + leftLabel: '0', + rightLabel: '100', + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + )..updateSliderThemeData((data) => data.copyWith( + activeTickMarkColor: const Color(0xFFE7E7E7), + inactiveTickMarkColor: const Color(0xFFE7E7E7), + )), + value: 60, + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + )..updateSliderThemeData((data) => data.copyWith( + activeTickMarkColor: const Color(0xFFE7E7E7), + inactiveTickMarkColor: const Color(0xFFE7E7E7), + )), + value: const RangeValues(20, 60), + // divisions: 5, + onChanged: (value) {}, + ) + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildCapsuleDoubleHandle.txt b/tdesign-component/example/assets/code/slider._buildCapsuleDoubleHandle.txt new file mode 100644 index 000000000..eed5f11bb --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildCapsuleDoubleHandle.txt @@ -0,0 +1,13 @@ + + Widget _buildCapsuleDoubleHandle(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + onChanged: (value) {}, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildCapsuleDoubleHandleWithNumber.txt b/tdesign-component/example/assets/code/slider._buildCapsuleDoubleHandleWithNumber.txt new file mode 100644 index 000000000..67e36216b --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildCapsuleDoubleHandleWithNumber.txt @@ -0,0 +1,16 @@ + + Widget _buildCapsuleDoubleHandleWithNumber(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showThumbValue: true, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + leftLabel: '0', + rightLabel: '100', + value: const RangeValues(20, 60), + onChanged: (value) {}, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildCapsuleDoubleHandleWithScale.txt b/tdesign-component/example/assets/code/slider._buildCapsuleDoubleHandleWithScale.txt new file mode 100644 index 000000000..b88140354 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildCapsuleDoubleHandleWithScale.txt @@ -0,0 +1,18 @@ + + Widget _buildCapsuleDoubleHandleWithScale(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + )..updateSliderThemeData((data) => data.copyWith( + activeTickMarkColor: const Color(0xFFE7E7E7), + inactiveTickMarkColor: const Color(0xFFE7E7E7), + )), + value: const RangeValues(20, 60), + onChanged: (value) {}, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildCapsuleSingleHandle.txt b/tdesign-component/example/assets/code/slider._buildCapsuleSingleHandle.txt new file mode 100644 index 000000000..5492520fa --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildCapsuleSingleHandle.txt @@ -0,0 +1,15 @@ + + Widget _buildCapsuleSingleHandle(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + leftLabel: '0', + rightLabel: '100', + value: 40, + onChanged: (value) {}, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildCapsuleSingleHandleWithNumber.txt b/tdesign-component/example/assets/code/slider._buildCapsuleSingleHandleWithNumber.txt new file mode 100644 index 000000000..ed0bcefa2 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildCapsuleSingleHandleWithNumber.txt @@ -0,0 +1,14 @@ + + Widget _buildCapsuleSingleHandleWithNumber(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showThumbValue: true, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: 40, + onChanged: (value) {}, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildCapsuleSingleHandleWithScale.txt b/tdesign-component/example/assets/code/slider._buildCapsuleSingleHandleWithScale.txt new file mode 100644 index 000000000..ec50695c1 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildCapsuleSingleHandleWithScale.txt @@ -0,0 +1,18 @@ + + Widget _buildCapsuleSingleHandleWithScale(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + )..updateSliderThemeData((data) => data.copyWith( + activeTickMarkColor: const Color(0xFFE7E7E7), + inactiveTickMarkColor: const Color(0xFFE7E7E7), + )), + value: 60, + onChanged: (value) {}, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildCustomActiveColor.txt b/tdesign-component/example/assets/code/slider._buildCustomActiveColor.txt new file mode 100644 index 000000000..b29fc1913 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildCustomActiveColor.txt @@ -0,0 +1,34 @@ + + Widget _buildCustomActiveColor(BuildContext context) { + return Column( + children: [ + TDSlider( + sliderThemeData: TDSliderThemeData( + activeTrackColor: Colors.red, + inactiveTrackColor: Colors.green, + context: context, + min: 0, + max: 100, + ), + value: 40, + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + activeTrackColor: Colors.green, + inactiveTrackColor: Colors.red, + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + onChanged: (value) {}, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildCustomDecoration.txt b/tdesign-component/example/assets/code/slider._buildCustomDecoration.txt new file mode 100644 index 000000000..823116e33 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildCustomDecoration.txt @@ -0,0 +1,32 @@ + + Widget _buildCustomDecoration(BuildContext context) { + return Column( + children: [ + TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + ), + value: 40, + boxDecoration: const BoxDecoration(color: Colors.amber), + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + boxDecoration: const BoxDecoration(color: Colors.deepOrangeAccent), + value: const RangeValues(20, 60), + onChanged: (value) {}, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildDisable.txt b/tdesign-component/example/assets/code/slider._buildDisable.txt new file mode 100644 index 000000000..51d09d0ed --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildDisable.txt @@ -0,0 +1,46 @@ + + Widget _buildDisable(BuildContext context) { + return Column( + children: [ + TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + ), + value: 40, + leftLabel: '0', + rightLabel: '100', + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + showThumbValue: true, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + leftLabel: '0', + rightLabel: '100', + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildDisableDoubleHandleWithNumber.txt b/tdesign-component/example/assets/code/slider._buildDisableDoubleHandleWithNumber.txt new file mode 100644 index 000000000..199044734 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildDisableDoubleHandleWithNumber.txt @@ -0,0 +1,15 @@ + + Widget _buildDisableDoubleHandleWithNumber(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showThumbValue: true, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + leftLabel: '0', + rightLabel: '100', + value: const RangeValues(20, 60), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildDisableDoubleHandleWithScale.txt b/tdesign-component/example/assets/code/slider._buildDisableDoubleHandleWithScale.txt new file mode 100644 index 000000000..1f199106f --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildDisableDoubleHandleWithScale.txt @@ -0,0 +1,14 @@ + + Widget _buildDisableDoubleHandleWithScale(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildDisableSingleHandle.txt b/tdesign-component/example/assets/code/slider._buildDisableSingleHandle.txt new file mode 100644 index 000000000..92b02c78d --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildDisableSingleHandle.txt @@ -0,0 +1,13 @@ + + Widget _buildDisableSingleHandle(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + ), + leftLabel: '0', + rightLabel: '100', + value: 40, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildDoubleHandle.txt b/tdesign-component/example/assets/code/slider._buildDoubleHandle.txt new file mode 100644 index 000000000..57b623f61 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildDoubleHandle.txt @@ -0,0 +1,12 @@ + + Widget _buildDoubleHandle(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + ), + value: const RangeValues(10, 60), + onChanged: (value) {}, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildDoubleHandleWithNumber.txt b/tdesign-component/example/assets/code/slider._buildDoubleHandleWithNumber.txt new file mode 100644 index 000000000..9c2ec88e9 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildDoubleHandleWithNumber.txt @@ -0,0 +1,16 @@ + + Widget _buildDoubleHandleWithNumber(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showThumbValue: true, + min: 0, + max: 100, + scaleFormatter: (value) => value.round().toString(), + ), + leftLabel: '0', + rightLabel: '100', + value: const RangeValues(40, 60), + onChanged: (value) {}, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildDoubleHandleWithScale.txt b/tdesign-component/example/assets/code/slider._buildDoubleHandleWithScale.txt new file mode 100644 index 000000000..2411ebe33 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildDoubleHandleWithScale.txt @@ -0,0 +1,15 @@ + + Widget _buildDoubleHandleWithScale(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(40, 70), + onChanged: (value) {}, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildOnTapDoubleHandle.txt b/tdesign-component/example/assets/code/slider._buildOnTapDoubleHandle.txt new file mode 100644 index 000000000..998a5b0ee --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildOnTapDoubleHandle.txt @@ -0,0 +1,50 @@ + + Widget _buildOnTapDoubleHandle(BuildContext context) { + final displayRangeDataNotifier = ValueNotifier( + DisplayRangeData( + currentPosition: Position.start, + currentTapValue: 40.0, + tapOffset: null, + ), + ); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ValueListenableBuilder( + valueListenable: displayRangeDataNotifier, + builder: (context, data, child) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Position: ${data.currentPosition}'), + const SizedBox(width: 10), + Text('Value: ${data.currentTapValue.toStringAsFixed(1)}'), + const SizedBox(width: 10), + if (data.tapOffset != null) + Text( + 'Tap at (${data.tapOffset!.dx.toStringAsFixed(0)}, ${data.tapOffset!.dy.toStringAsFixed(0)})'), + ], + ); + }, + ), + const SizedBox(height: 10), + TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, min: 0, max: 100, showThumbValue: true), + leftLabel: '0', + rightLabel: '100', + value: const RangeValues(10, 60), + onChanged: (value) {}, + onTap: (position, offset, value) { + displayRangeDataNotifier.value = DisplayRangeData( + currentPosition: position, + currentTapValue: value, + tapOffset: offset, + ); + print('onTap offset: $offset, value: $value'); + }, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildOnTapSingleHandle.txt b/tdesign-component/example/assets/code/slider._buildOnTapSingleHandle.txt new file mode 100644 index 000000000..688840858 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildOnTapSingleHandle.txt @@ -0,0 +1,40 @@ + + Widget _buildOnTapSingleHandle(BuildContext context) { + var currentValue = 40.0; + Offset? tapOffset; + + return StatefulBuilder( + builder: (context, setState) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Value: ${currentValue.toStringAsFixed(1)}'), + const SizedBox(width: 10), + if (tapOffset != null) + Text( + 'Tap at (${tapOffset!.dx.toStringAsFixed(0)}, ${tapOffset!.dy.toStringAsFixed(0)})'), + ], + ), + TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, min: 0, max: 100, showThumbValue: true), + leftLabel: '0', + rightLabel: '100', + value: currentValue, + onChanged: (value) {}, + onTap: (offset, value) { + setState(() { + currentValue = value; + tapOffset = offset; + }); + print('onTap offset: $offset, value: $value'); + }, + ), + ], + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildOnThumbTextTapDoubleHandle.txt b/tdesign-component/example/assets/code/slider._buildOnThumbTextTapDoubleHandle.txt new file mode 100644 index 000000000..24ff47cba --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildOnThumbTextTapDoubleHandle.txt @@ -0,0 +1,50 @@ + + Widget _buildOnThumbTextTapDoubleHandle(BuildContext context) { + final displayRangeDataNotifier = ValueNotifier( + DisplayRangeData( + currentPosition: Position.start, + currentTapValue: 40.0, + tapOffset: null, + ), + ); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ValueListenableBuilder( + valueListenable: displayRangeDataNotifier, + builder: (context, data, child) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Position: ${data.currentPosition}'), + const SizedBox(width: 10), + Text('Value: ${data.currentTapValue.toStringAsFixed(1)}'), + const SizedBox(width: 10), + if (data.tapOffset != null) + Text( + 'Tap at (${data.tapOffset!.dx.toStringAsFixed(0)}, ${data.tapOffset!.dy.toStringAsFixed(0)})'), + ], + ); + }, + ), + const SizedBox(height: 10), + TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, min: 0, max: 100, showThumbValue: true), + leftLabel: '0', + rightLabel: '100', + value: const RangeValues(10, 60), + onChanged: (value) {}, + onThumbTextTap: (position, offset, value) { + displayRangeDataNotifier.value = DisplayRangeData( + currentPosition: position, + currentTapValue: value, + tapOffset: offset, + ); + print('onTap offset: $offset, value: $value'); + }, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildOnThumbTextTapSingleHandle.txt b/tdesign-component/example/assets/code/slider._buildOnThumbTextTapSingleHandle.txt new file mode 100644 index 000000000..565820635 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildOnThumbTextTapSingleHandle.txt @@ -0,0 +1,44 @@ + + Widget _buildOnThumbTextTapSingleHandle(BuildContext context) { + var currentValue = 40.0; + Offset? tapOffset; + + return StatefulBuilder( + builder: (context, setState) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Value: ${currentValue.toStringAsFixed(1)}'), + const SizedBox(width: 10), + if (tapOffset != null) + Text( + 'Tap at (${tapOffset!.dx.toStringAsFixed(0)}, ${tapOffset!.dy.toStringAsFixed(0)})'), + ], + ), + TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + showThumbValue: true, + ), + leftLabel: '0', + rightLabel: '100', + value: currentValue, + onChanged: (value) {}, + onThumbTextTap: (offset, value) { + setState(() { + currentValue = value; + tapOffset = offset; + }); + print('onTap offset: $offset, value: $value'); + }, + ), + ], + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildSingleHandle.txt b/tdesign-component/example/assets/code/slider._buildSingleHandle.txt new file mode 100644 index 000000000..960a5067a --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildSingleHandle.txt @@ -0,0 +1,11 @@ + + Widget _buildSingleHandle(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + ), + value: 10, + onChanged: (value) {}); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildSingleHandleWithNumber.txt b/tdesign-component/example/assets/code/slider._buildSingleHandleWithNumber.txt new file mode 100644 index 000000000..5cdab2842 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildSingleHandleWithNumber.txt @@ -0,0 +1,16 @@ + + Widget _buildSingleHandleWithNumber(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showThumbValue: true, + scaleFormatter: (value) => value.toInt().toString(), + min: 0, + max: 100, + ), + value: 10, + leftLabel: '0', + rightLabel: '100', + onChanged: (value) {}, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/slider._buildSingleHandleWithScale.txt b/tdesign-component/example/assets/code/slider._buildSingleHandleWithScale.txt new file mode 100644 index 000000000..9407ed609 --- /dev/null +++ b/tdesign-component/example/assets/code/slider._buildSingleHandleWithScale.txt @@ -0,0 +1,15 @@ + + Widget _buildSingleHandleWithScale(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: 60, + onChanged: (value) {}, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/stepper._buildRow.txt b/tdesign-component/example/assets/code/stepper._buildRow.txt new file mode 100644 index 000000000..138512969 --- /dev/null +++ b/tdesign-component/example/assets/code/stepper._buildRow.txt @@ -0,0 +1,22 @@ + + Widget _buildRow(BuildContext context, List stepperItems) { + final theme = TDTheme.of(context); + + return Container( + decoration: BoxDecoration( + color: theme.whiteColor1, + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: stepperItems + .map((item) => SizedBox( + width: (MediaQuery.of(context).size.width - 32) / 3, + child: item, + )) + .toList(), + ), + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/stepper._buildStepperWithBase.txt b/tdesign-component/example/assets/code/stepper._buildStepperWithBase.txt new file mode 100644 index 000000000..a91a7d8e8 --- /dev/null +++ b/tdesign-component/example/assets/code/stepper._buildStepperWithBase.txt @@ -0,0 +1,8 @@ + + Widget _buildStepperWithBase(BuildContext context) { + return _buildRow(context, [ + const TDStepper( + theme: TDStepperTheme.filled, + ) + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/stepper._buildStepperWithDisableStatus.txt b/tdesign-component/example/assets/code/stepper._buildStepperWithDisableStatus.txt new file mode 100644 index 000000000..ef86ebd0d --- /dev/null +++ b/tdesign-component/example/assets/code/stepper._buildStepperWithDisableStatus.txt @@ -0,0 +1,9 @@ + + Widget _buildStepperWithDisableStatus(BuildContext context) { + return _buildRow(context, [ + const TDStepper( + theme: TDStepperTheme.filled, + disabled: true, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/stepper._buildStepperWithMaxMinStatus.txt b/tdesign-component/example/assets/code/stepper._buildStepperWithMaxMinStatus.txt new file mode 100644 index 000000000..b68a89866 --- /dev/null +++ b/tdesign-component/example/assets/code/stepper._buildStepperWithMaxMinStatus.txt @@ -0,0 +1,7 @@ + + Widget _buildStepperWithMaxMinStatus(BuildContext context) { + return _buildRow(context, [ + const TDStepper(theme: TDStepperTheme.filled, value: 0, min: 0), + const TDStepper(theme: TDStepperTheme.filled, value: 999, max: 999), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/stepper._buildStepperWithSize.txt b/tdesign-component/example/assets/code/stepper._buildStepperWithSize.txt new file mode 100644 index 000000000..8137d4974 --- /dev/null +++ b/tdesign-component/example/assets/code/stepper._buildStepperWithSize.txt @@ -0,0 +1,11 @@ + + Widget _buildStepperWithSize(BuildContext context) { + return _buildRow(context, [ + const TDStepper( + size: TDStepperSize.large, theme: TDStepperTheme.filled, value: 3), + const TDStepper( + size: TDStepperSize.medium, theme: TDStepperTheme.filled, value: 3), + const TDStepper( + size: TDStepperSize.small, theme: TDStepperTheme.filled, value: 3), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/stepper._buildStepperWithTheme.txt b/tdesign-component/example/assets/code/stepper._buildStepperWithTheme.txt new file mode 100644 index 000000000..08c2ddd20 --- /dev/null +++ b/tdesign-component/example/assets/code/stepper._buildStepperWithTheme.txt @@ -0,0 +1,8 @@ + + Widget _buildStepperWithTheme(BuildContext context) { + return _buildRow(context, [ + const TDStepper(theme: TDStepperTheme.filled, value: 3), + const TDStepper(theme: TDStepperTheme.outline, value: 3), + const TDStepper(theme: TDStepperTheme.normal, value: 3), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildBasicHSteps1.txt b/tdesign-component/example/assets/code/steps._buildBasicHSteps1.txt new file mode 100644 index 000000000..d838b4e2f --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildBasicHSteps1.txt @@ -0,0 +1,16 @@ + + Widget _buildBasicHSteps1(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: basicHStepsListData1, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildBasicHSteps2.txt b/tdesign-component/example/assets/code/steps._buildBasicHSteps2.txt new file mode 100644 index 000000000..137b40243 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildBasicHSteps2.txt @@ -0,0 +1,18 @@ + + Widget _buildBasicHSteps2(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: basicHStepsListData2, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildBasicHSteps3.txt b/tdesign-component/example/assets/code/steps._buildBasicHSteps3.txt new file mode 100644 index 000000000..73ba9b884 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildBasicHSteps3.txt @@ -0,0 +1,18 @@ + + Widget _buildBasicHSteps3(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: basicHStepsListData3, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildHErrorSteps1.txt b/tdesign-component/example/assets/code/steps._buildHErrorSteps1.txt new file mode 100644 index 000000000..c40a5cf1b --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildHErrorSteps1.txt @@ -0,0 +1,19 @@ + + Widget _buildHErrorSteps1(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hErrorStepsListData1, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + status: TDStepsStatus.error, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildHErrorSteps2.txt b/tdesign-component/example/assets/code/steps._buildHErrorSteps2.txt new file mode 100644 index 000000000..39566b701 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildHErrorSteps2.txt @@ -0,0 +1,19 @@ + + Widget _buildHErrorSteps2(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hErrorStepsListData2, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + status: TDStepsStatus.error, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildHErrorSteps3.txt b/tdesign-component/example/assets/code/steps._buildHErrorSteps3.txt new file mode 100644 index 000000000..183386439 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildHErrorSteps3.txt @@ -0,0 +1,20 @@ + + Widget _buildHErrorSteps3(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hErrorStepsListData3, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + status: TDStepsStatus.error, + simple: true, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildHIconSteps1.txt b/tdesign-component/example/assets/code/steps._buildHIconSteps1.txt new file mode 100644 index 000000000..abcd6c616 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildHIconSteps1.txt @@ -0,0 +1,18 @@ + + Widget _buildHIconSteps1(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hIconStepsListData1, + direction: TDStepsDirection.horizontal, + activeIndex: 0, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildHIconSteps2.txt b/tdesign-component/example/assets/code/steps._buildHIconSteps2.txt new file mode 100644 index 000000000..0b17b7cdf --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildHIconSteps2.txt @@ -0,0 +1,18 @@ + + Widget _buildHIconSteps2(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hIconStepsListData2, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildHIconSteps3.txt b/tdesign-component/example/assets/code/steps._buildHIconSteps3.txt new file mode 100644 index 000000000..6671f9163 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildHIconSteps3.txt @@ -0,0 +1,18 @@ + + Widget _buildHIconSteps3(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hIconStepsListData3, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildHReadOnlySteps.txt b/tdesign-component/example/assets/code/steps._buildHReadOnlySteps.txt new file mode 100644 index 000000000..daccf9e0e --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildHReadOnlySteps.txt @@ -0,0 +1,17 @@ + + Widget _buildHReadOnlySteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hReadOnlyStepsListData, + readOnly: true, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildSimpleHSteps1.txt b/tdesign-component/example/assets/code/steps._buildSimpleHSteps1.txt new file mode 100644 index 000000000..28a7b27ab --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildSimpleHSteps1.txt @@ -0,0 +1,19 @@ + + Widget _buildSimpleHSteps1(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: simpleHStepsListData1, + direction: TDStepsDirection.horizontal, + activeIndex: 0, + simple: true, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildSimpleHSteps2.txt b/tdesign-component/example/assets/code/steps._buildSimpleHSteps2.txt new file mode 100644 index 000000000..7d6609a2e --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildSimpleHSteps2.txt @@ -0,0 +1,19 @@ + + Widget _buildSimpleHSteps2(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: simpleHStepsListData2, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + simple: true, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildSimpleHSteps3.txt b/tdesign-component/example/assets/code/steps._buildSimpleHSteps3.txt new file mode 100644 index 000000000..1dfe89db2 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildSimpleHSteps3.txt @@ -0,0 +1,19 @@ + + Widget _buildSimpleHSteps3(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: simpleHStepsListData3, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + simple: true, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildVBasicSteps.txt b/tdesign-component/example/assets/code/steps._buildVBasicSteps.txt new file mode 100644 index 000000000..42576aab5 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildVBasicSteps.txt @@ -0,0 +1,18 @@ + + Widget _buildVBasicSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vBasicStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildVCustomContentBaseSteps.txt b/tdesign-component/example/assets/code/steps._buildVCustomContentBaseSteps.txt new file mode 100644 index 000000000..b62bf5dde --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildVCustomContentBaseSteps.txt @@ -0,0 +1,18 @@ + + Widget _buildVCustomContentBaseSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vCustomContentBasicStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildVCustomTitleBaseSteps.txt b/tdesign-component/example/assets/code/steps._buildVCustomTitleBaseSteps.txt new file mode 100644 index 000000000..8d6f7aa04 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildVCustomTitleBaseSteps.txt @@ -0,0 +1,18 @@ + + Widget _buildVCustomTitleBaseSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vCustomTitleBasicStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildVCustomizeSteps.txt b/tdesign-component/example/assets/code/steps._buildVCustomizeSteps.txt new file mode 100644 index 000000000..ccb417220 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildVCustomizeSteps.txt @@ -0,0 +1,20 @@ + + Widget _buildVCustomizeSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vCustomizeStepsListData, + direction: TDStepsDirection.vertical, + simple: true, + activeIndex: 3, + verticalSelect: true, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildVErrorBasicSteps.txt b/tdesign-component/example/assets/code/steps._buildVErrorBasicSteps.txt new file mode 100644 index 000000000..a90d2dba1 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildVErrorBasicSteps.txt @@ -0,0 +1,19 @@ + + Widget _buildVErrorBasicSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vErrorBasicStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + status: TDStepsStatus.error, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildVErrorIconSteps.txt b/tdesign-component/example/assets/code/steps._buildVErrorIconSteps.txt new file mode 100644 index 000000000..16b4e1ae4 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildVErrorIconSteps.txt @@ -0,0 +1,19 @@ + + Widget _buildVErrorIconSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vErrorIconStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + status: TDStepsStatus.error, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildVErrorSimpleSteps.txt b/tdesign-component/example/assets/code/steps._buildVErrorSimpleSteps.txt new file mode 100644 index 000000000..78d3b07cb --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildVErrorSimpleSteps.txt @@ -0,0 +1,20 @@ + + Widget _buildVErrorSimpleSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vErrorSimpleStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + simple: true, + status: TDStepsStatus.error, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildVIconSteps.txt b/tdesign-component/example/assets/code/steps._buildVIconSteps.txt new file mode 100644 index 000000000..7938855a1 --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildVIconSteps.txt @@ -0,0 +1,18 @@ + + Widget _buildVIconSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vIconStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildVReadOnlySteps.txt b/tdesign-component/example/assets/code/steps._buildVReadOnlySteps.txt new file mode 100644 index 000000000..666ac049d --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildVReadOnlySteps.txt @@ -0,0 +1,19 @@ + + Widget _buildVReadOnlySteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vReadOnlyStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 0, + readOnly: true, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/steps._buildVSimpleSteps.txt b/tdesign-component/example/assets/code/steps._buildVSimpleSteps.txt new file mode 100644 index 000000000..252f1fa9d --- /dev/null +++ b/tdesign-component/example/assets/code/steps._buildVSimpleSteps.txt @@ -0,0 +1,19 @@ + + Widget _buildVSimpleSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vSimpleStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + simple: true, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swipecell._buildSwiper3Cell.txt b/tdesign-component/example/assets/code/swipecell._buildSwiper3Cell.txt new file mode 100644 index 000000000..8ab07b297 --- /dev/null +++ b/tdesign-component/example/assets/code/swipecell._buildSwiper3Cell.txt @@ -0,0 +1,32 @@ + + Widget _buildSwiper3Cell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: 180 / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).brandColor7, + label: '保存', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + label: '编辑', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + label: '删除', + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swipecell._buildSwiperCell.txt b/tdesign-component/example/assets/code/swipecell._buildSwiperCell.txt new file mode 100644 index 000000000..faebda025 --- /dev/null +++ b/tdesign-component/example/assets/code/swipecell._buildSwiperCell.txt @@ -0,0 +1,50 @@ + + Widget _buildSwiperCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + var list = [ + {'id': '1', 'title': '左滑操作', 'note': '辅助信息', 'description': ''}, + {'id': '2', 'title': '左滑操作', 'note': '辅助信息', 'description': '一段很长很长的内容文字'}, + ]; + final cellLength = ValueNotifier(list.length); + return ValueListenableBuilder( + valueListenable: cellLength, + builder: (BuildContext context, value, Widget? child) { + return TDCellGroup( + cells: list.map((e) => TDCell(title: e['title'], note: e['note'], description: e['description'])).toList(), + builder: (context, cell, index) { + return TDSwipeCell( + slidableKey: ValueKey(list[index]['id']), + groupTag: 'test', + onChange: (direction, open) { + print('打开方向:$direction'); + print('打开转态$open'); + }, + right: TDSwipeCellPanel( + extentRatio: 60 / screenWidth, + // dragDismissible: true, + onDismissed: (context) { + list.removeAt(index); + cellLength.value = list.length; + }, + children: [ + TDSwipeCellAction( + backgroundColor: TDTheme.of(context).errorColor6, + label: '删除', + onPressed: (context) { + print('点击action'); + print(TDSwipeCell.of(context)); + print(TDSwipeCellInherited.of(context)?.controller); + list.removeAt(index); + cellLength.value = list.length; + }, + ), + ], + ), + cell: cell, + ); + }, + ); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swipecell._buildSwiperConfirmCell.txt b/tdesign-component/example/assets/code/swipecell._buildSwiperConfirmCell.txt new file mode 100644 index 000000000..185af4ab0 --- /dev/null +++ b/tdesign-component/example/assets/code/swipecell._buildSwiperConfirmCell.txt @@ -0,0 +1,34 @@ + + Widget _buildSwiperConfirmCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: (60 + 60) / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + label: '编辑', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + label: '删除', + ), + ], + confirms: [ + TDSwipeCellAction( + backgroundColor: TDTheme.of(context).errorColor6, + label: '确认删除', + confirmIndex: const [1], + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swipecell._buildSwiperIconCell.txt b/tdesign-component/example/assets/code/swipecell._buildSwiperIconCell.txt new file mode 100644 index 000000000..763486ef0 --- /dev/null +++ b/tdesign-component/example/assets/code/swipecell._buildSwiperIconCell.txt @@ -0,0 +1,92 @@ + + Widget _buildSwiperIconCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: 3, + itemBuilder: (context, index) { + if (index == 0) { + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: (80 + 80) / screenWidth, + children: [ + TDSwipeCellAction( + flex: 80, + backgroundColor: TDTheme.of(context).warningColor4, + icon: TDIcons.edit, + label: '编辑', + ), + TDSwipeCellAction( + flex: 80, + backgroundColor: TDTheme.of(context).errorColor6, + icon: TDIcons.delete, + label: '删除', + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + ), + ); + } else if (index == 1) { + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: 120 / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + icon: TDIcons.edit, + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + icon: TDIcons.delete, + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + ), + ); + } else { + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: 120 / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + direction: Axis.vertical, + icon: TDIcons.edit, + label: '编辑', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + direction: Axis.vertical, + icon: TDIcons.delete, + label: '删除', + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + description: '一段很长很长的内容文字', + ), + ); + } + }, + separatorBuilder: (context, index) { + return const SizedBox(height: 24); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swipecell._buildSwiperMuliCell.txt b/tdesign-component/example/assets/code/swipecell._buildSwiperMuliCell.txt new file mode 100644 index 000000000..7ab870063 --- /dev/null +++ b/tdesign-component/example/assets/code/swipecell._buildSwiperMuliCell.txt @@ -0,0 +1,27 @@ + + Widget _buildSwiperMuliCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: 120 / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + label: '编辑', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + label: '删除', + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swipecell._buildSwiperRightCell.txt b/tdesign-component/example/assets/code/swipecell._buildSwiperRightCell.txt new file mode 100644 index 000000000..acca4bcf8 --- /dev/null +++ b/tdesign-component/example/assets/code/swipecell._buildSwiperRightCell.txt @@ -0,0 +1,21 @@ + + Widget _buildSwiperRightCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return TDSwipeCell( + groupTag: 'test', + left: TDSwipeCellPanel( + extentRatio: 60 / screenWidth, + children: [ + TDSwipeCellAction( + backgroundColor: TDTheme.of(context).brandColor7, + label: '选择', + ), + ], + ), + cell: const TDCell( + title: '右滑操作', + note: '辅助信息', + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swipecell._buildSwiperRightLeftCell.txt b/tdesign-component/example/assets/code/swipecell._buildSwiperRightLeftCell.txt new file mode 100644 index 000000000..201c4287b --- /dev/null +++ b/tdesign-component/example/assets/code/swipecell._buildSwiperRightLeftCell.txt @@ -0,0 +1,36 @@ + + Widget _buildSwiperRightLeftCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return TDSwipeCell( + groupTag: 'test', + left: TDSwipeCellPanel( + extentRatio: 60 / screenWidth, + children: [ + TDSwipeCellAction( + backgroundColor: TDTheme.of(context).brandColor7, + label: '选择', + ), + ], + ), + right: TDSwipeCellPanel( + extentRatio: 120 / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + label: '编辑', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + label: '删除', + ), + ], + ), + cell: const TDCell( + title: '左右滑操作', + note: '辅助信息', + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildCardsSwiper.txt b/tdesign-component/example/assets/code/swiper._buildCardsSwiper.txt new file mode 100644 index 000000000..014ce3674 --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildCardsSwiper.txt @@ -0,0 +1,17 @@ + + Widget _buildCardsSwiper(BuildContext context) { + return Swiper( + viewportFraction: 0.75, + outer: true, + autoplay: true, + itemCount: 6, + loop: true, + transformer: TDPageTransformer.margin(), + pagination: const SwiperPagination( + alignment: Alignment.center, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildControlsSwiper.txt b/tdesign-component/example/assets/code/swiper._buildControlsSwiper.txt new file mode 100644 index 000000000..a4894abce --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildControlsSwiper.txt @@ -0,0 +1,14 @@ + + Widget _buildControlsSwiper(BuildContext context) { + return Swiper( + // autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.center, + builder: TDSwiperPagination.controls), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildDotsBarSwiper.txt b/tdesign-component/example/assets/code/swiper._buildDotsBarSwiper.txt new file mode 100644 index 000000000..f7b8bf895 --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildDotsBarSwiper.txt @@ -0,0 +1,14 @@ + + Widget _buildDotsBarSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + builder: TDSwiperPagination.dotsBar), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildDotsSwiper.txt b/tdesign-component/example/assets/code/swiper._buildDotsSwiper.txt new file mode 100644 index 000000000..baef70151 --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildDotsSwiper.txt @@ -0,0 +1,14 @@ + + Widget _buildDotsSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildFractionBarSwiper.txt b/tdesign-component/example/assets/code/swiper._buildFractionBarSwiper.txt new file mode 100644 index 000000000..66a9d2aee --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildFractionBarSwiper.txt @@ -0,0 +1,14 @@ + + Widget _buildFractionBarSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.bottomRight, + builder: TDSwiperPagination.fraction), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildFractionSwiper.txt b/tdesign-component/example/assets/code/swiper._buildFractionSwiper.txt new file mode 100644 index 000000000..9df2c7fa8 --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildFractionSwiper.txt @@ -0,0 +1,14 @@ + + Widget _buildFractionSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + builder: TDSwiperPagination.fraction), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildNotLoopCardsSwiper.txt b/tdesign-component/example/assets/code/swiper._buildNotLoopCardsSwiper.txt new file mode 100644 index 000000000..ebdd87e0b --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildNotLoopCardsSwiper.txt @@ -0,0 +1,17 @@ + + Widget _buildNotLoopCardsSwiper(BuildContext context) { + return Swiper( + viewportFraction: 0.75, + scale: 0.8, + outer: true, + autoplay: true, + itemCount: 2, + loop: false, + pagination: const SwiperPagination( + alignment: Alignment.center, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildOuterDotsBarSwiper.txt b/tdesign-component/example/assets/code/swiper._buildOuterDotsBarSwiper.txt new file mode 100644 index 000000000..e5dfe7540 --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildOuterDotsBarSwiper.txt @@ -0,0 +1,15 @@ + + Widget _buildOuterDotsBarSwiper(BuildContext context) { + return Swiper( + outer: true, + autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.topLeft, + builder: TDSwiperPagination.dotsBar), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildOuterDotsSwiper.txt b/tdesign-component/example/assets/code/swiper._buildOuterDotsSwiper.txt new file mode 100644 index 000000000..08bb770f8 --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildOuterDotsSwiper.txt @@ -0,0 +1,15 @@ + + Widget _buildOuterDotsSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + outer: true, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildRightDotsSwiper.txt b/tdesign-component/example/assets/code/swiper._buildRightDotsSwiper.txt new file mode 100644 index 000000000..3bf57b074 --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildRightDotsSwiper.txt @@ -0,0 +1,15 @@ + + Widget _buildRightDotsSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + scrollDirection: Axis.vertical, + pagination: const SwiperPagination( + alignment: Alignment.centerRight, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildScaleCardsSwiper.txt b/tdesign-component/example/assets/code/swiper._buildScaleCardsSwiper.txt new file mode 100644 index 000000000..e728563e0 --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildScaleCardsSwiper.txt @@ -0,0 +1,17 @@ + + Widget _buildScaleCardsSwiper(BuildContext context) { + return Swiper( + viewportFraction: 0.75, + outer: true, + autoplay: true, + itemCount: 6, + loop: true, + transformer: TDPageTransformer.scaleAndFade(), + pagination: const SwiperPagination( + alignment: Alignment.center, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/swiper._buildVerticalDotsBarSwiper.txt b/tdesign-component/example/assets/code/swiper._buildVerticalDotsBarSwiper.txt new file mode 100644 index 000000000..835f35b9b --- /dev/null +++ b/tdesign-component/example/assets/code/swiper._buildVerticalDotsBarSwiper.txt @@ -0,0 +1,15 @@ + + Widget _buildVerticalDotsBarSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + scrollDirection:Axis.vertical, + pagination: const SwiperPagination( + alignment: Alignment.bottomRight, + builder: TDSwiperPagination.dotsBar), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildItem.txt b/tdesign-component/example/assets/code/switch._buildItem.txt new file mode 100644 index 000000000..30ac282bb --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildItem.txt @@ -0,0 +1,33 @@ + + Widget _buildItem(BuildContext context, Widget switchItem, + {String? title, String? desc}) { + final theme = TDTheme.of(context); + Widget current = Row( + children: [ + Expanded( + child: TDText( + title ?? '', + textColor: theme.fontGyColor1, + )), + TDText( + desc ?? '', + textColor: theme.grayColor6, + ), + SizedBox(child: switchItem) + ], + ); + current = Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: SizedBox( + child: Container( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: current, + ), + color: Colors.white, + ), + height: 56, + ), + ); + return Column(mainAxisSize: MainAxisSize.min, children: [current]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitch.txt b/tdesign-component/example/assets/code/switch._buildSwitch.txt new file mode 100644 index 000000000..777cbca2e --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitch.txt @@ -0,0 +1,22 @@ + + Widget _buildSwitch({ + bool on = true, + bool enable = true, + Color? trackOnColor, + Color? trackOffColor, + Color? thumbContentOnColor, + Color? thumbContentOffColor, + TDSwitchSize? size, + TDSwitchType? type, + }) { + return TDSwitch( + isOn: on, + trackOnColor: trackOnColor, + trackOffColor: trackOffColor, + thumbContentOnColor: thumbContentOnColor, + thumbContentOffColor: thumbContentOffColor, + enable: enable, + size: size, + type: type, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitchWithBase.txt b/tdesign-component/example/assets/code/switch._buildSwitchWithBase.txt new file mode 100644 index 000000000..4d3d5ba68 --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitchWithBase.txt @@ -0,0 +1,8 @@ + + Widget _buildSwitchWithBase(BuildContext context) { + return _buildItem( + context, + const TDSwitch(), + title: '基础开关', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitchWithColor.txt b/tdesign-component/example/assets/code/switch._buildSwitchWithColor.txt new file mode 100644 index 000000000..1ffa265c1 --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitchWithColor.txt @@ -0,0 +1,8 @@ + + Widget _buildSwitchWithColor(BuildContext context) { + return _buildItem( + context, + const TDSwitch(isOn: true, trackOnColor: Colors.green), + title: '自定义颜色开关', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitchWithDisableOff.txt b/tdesign-component/example/assets/code/switch._buildSwitchWithDisableOff.txt new file mode 100644 index 000000000..8ecab6fc3 --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitchWithDisableOff.txt @@ -0,0 +1,11 @@ + + Widget _buildSwitchWithDisableOff(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + enable: false, + isOn: false, + ), + title: '禁用状态', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitchWithDisableOn.txt b/tdesign-component/example/assets/code/switch._buildSwitchWithDisableOn.txt new file mode 100644 index 000000000..7a3aa4612 --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitchWithDisableOn.txt @@ -0,0 +1,11 @@ + + Widget _buildSwitchWithDisableOn(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + enable: false, + isOn: true, + ), + title: '禁用状态', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitchWithIcon.txt b/tdesign-component/example/assets/code/switch._buildSwitchWithIcon.txt new file mode 100644 index 000000000..9623bf98b --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitchWithIcon.txt @@ -0,0 +1,8 @@ + + Widget _buildSwitchWithIcon(BuildContext context) { + return _buildItem( + context, + const TDSwitch(isOn: true, type: TDSwitchType.icon), + title: '带图标开关', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitchWithLoadingOff.txt b/tdesign-component/example/assets/code/switch._buildSwitchWithLoadingOff.txt new file mode 100644 index 000000000..e8cd834e2 --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitchWithLoadingOff.txt @@ -0,0 +1,11 @@ + + Widget _buildSwitchWithLoadingOff(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + isOn: false, + type: TDSwitchType.loading, + ), + title: '加载状态', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitchWithLoadingOn.txt b/tdesign-component/example/assets/code/switch._buildSwitchWithLoadingOn.txt new file mode 100644 index 000000000..461a269a6 --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitchWithLoadingOn.txt @@ -0,0 +1,11 @@ + + Widget _buildSwitchWithLoadingOn(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + isOn: true, + type: TDSwitchType.loading, + ), + title: '加载状态', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitchWithSizeLarge.txt b/tdesign-component/example/assets/code/switch._buildSwitchWithSizeLarge.txt new file mode 100644 index 000000000..129a0f449 --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitchWithSizeLarge.txt @@ -0,0 +1,11 @@ + + Widget _buildSwitchWithSizeLarge(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + isOn: true, + size: TDSwitchSize.large, + ), + title: '大尺寸32', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitchWithSizeMed.txt b/tdesign-component/example/assets/code/switch._buildSwitchWithSizeMed.txt new file mode 100644 index 000000000..c0154d7d5 --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitchWithSizeMed.txt @@ -0,0 +1,11 @@ + + Widget _buildSwitchWithSizeMed(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + isOn: true, + size: TDSwitchSize.medium, + ), + title: '中尺寸28', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitchWithSizeSmall.txt b/tdesign-component/example/assets/code/switch._buildSwitchWithSizeSmall.txt new file mode 100644 index 000000000..fe7221cfa --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitchWithSizeSmall.txt @@ -0,0 +1,11 @@ + + Widget _buildSwitchWithSizeSmall(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + isOn: true, + size: TDSwitchSize.small, + ), + title: '小尺寸24', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._buildSwitchWithText.txt b/tdesign-component/example/assets/code/switch._buildSwitchWithText.txt new file mode 100644 index 000000000..44f9acfbb --- /dev/null +++ b/tdesign-component/example/assets/code/switch._buildSwitchWithText.txt @@ -0,0 +1,8 @@ + + Widget _buildSwitchWithText(BuildContext context) { + return _buildItem( + context, + const TDSwitch(isOn: true, type: TDSwitchType.text), + title: '带文字开关', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._customText.txt b/tdesign-component/example/assets/code/switch._customText.txt new file mode 100644 index 000000000..a914998b8 --- /dev/null +++ b/tdesign-component/example/assets/code/switch._customText.txt @@ -0,0 +1,12 @@ + + Widget _customText(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + type: TDSwitchType.text, + openText: '1111', + closeText: '—', + ), + title: '基础开关', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/switch._customTextFont.txt b/tdesign-component/example/assets/code/switch._customTextFont.txt new file mode 100644 index 000000000..ca87352c3 --- /dev/null +++ b/tdesign-component/example/assets/code/switch._customTextFont.txt @@ -0,0 +1,16 @@ + + Widget _customTextFont(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + type: TDSwitchType.text, + openText: '开', + closeText: '关', + thumbContentOffColor: Colors.red, + thumbContentOnColor: Colors.green, + thumbContentOnFont: TextStyle(fontSize: 18), + thumbContentOffFont: TextStyle(fontSize: 12), + ), + title: '基础开关', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._basicTable.txt b/tdesign-component/example/assets/code/table._basicTable.txt new file mode 100644 index 000000000..b66105746 --- /dev/null +++ b/tdesign-component/example/assets/code/table._basicTable.txt @@ -0,0 +1,12 @@ + + Widget _basicTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + data: _getData(9), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._borderTable.txt b/tdesign-component/example/assets/code/table._borderTable.txt new file mode 100644 index 000000000..82b1f433a --- /dev/null +++ b/tdesign-component/example/assets/code/table._borderTable.txt @@ -0,0 +1,13 @@ + + Widget _borderTable(BuildContext context) { + return TDTable( + bordered: true, + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + data: _getData(9), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._centerTable.txt b/tdesign-component/example/assets/code/table._centerTable.txt new file mode 100644 index 000000000..8bbe11c9f --- /dev/null +++ b/tdesign-component/example/assets/code/table._centerTable.txt @@ -0,0 +1,12 @@ + + Widget _centerTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', align: TDTableColAlign.center), + TDTableCol(title: '标题', colKey: 'title2', align: TDTableColAlign.center), + TDTableCol(title: '标题', colKey: 'title3', align: TDTableColAlign.center), + TDTableCol(title: '标题', colKey: 'title4', align: TDTableColAlign.center) + ], + data: _getData(10), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._emptyTable.txt b/tdesign-component/example/assets/code/table._emptyTable.txt new file mode 100644 index 000000000..8b0ce5ce3 --- /dev/null +++ b/tdesign-component/example/assets/code/table._emptyTable.txt @@ -0,0 +1,11 @@ + + Widget _emptyTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1'), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._fixedEndColTable.txt b/tdesign-component/example/assets/code/table._fixedEndColTable.txt new file mode 100644 index 000000000..d1c762978 --- /dev/null +++ b/tdesign-component/example/assets/code/table._fixedEndColTable.txt @@ -0,0 +1,37 @@ + + Widget _fixedEndColTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1'), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol( + title: '标题', + colKey: 'title4', + fixed: TDTableColFixed.right, + cellBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TDText( + '修改', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + ), + ), + TDText( + '通过', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + ), + ), + ], + ); + }, + ), + ], + data: _getData(10), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._fixedFirstColTable.txt b/tdesign-component/example/assets/code/table._fixedFirstColTable.txt new file mode 100644 index 000000000..fb6fd8b71 --- /dev/null +++ b/tdesign-component/example/assets/code/table._fixedFirstColTable.txt @@ -0,0 +1,12 @@ + + Widget _fixedFirstColTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1'), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4', fixed: TDTableColFixed.left), + ], + data: _getData(10), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._fixedHeaderTable.txt b/tdesign-component/example/assets/code/table._fixedHeaderTable.txt new file mode 100644 index 000000000..10b24f605 --- /dev/null +++ b/tdesign-component/example/assets/code/table._fixedHeaderTable.txt @@ -0,0 +1,14 @@ + + Widget _fixedHeaderTable(BuildContext context) { + return TDTable( + bordered: true, + height: 240, + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + data: _getData(9), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._fixedScrollTable.txt b/tdesign-component/example/assets/code/table._fixedScrollTable.txt new file mode 100644 index 000000000..8d051d313 --- /dev/null +++ b/tdesign-component/example/assets/code/table._fixedScrollTable.txt @@ -0,0 +1,37 @@ + + Widget _fixedScrollTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', width: 200), + TDTableCol(title: '标题', colKey: 'title2', width: 160), + TDTableCol(title: '标题', colKey: 'title3', width: 160), + TDTableCol( + title: '标题', + colKey: 'title4', + fixed: TDTableColFixed.right, + cellBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TDText( + '修改', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + ), + ), + TDText( + '通过', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + ), + ), + ], + ); + }, + ), + ], + data: _getData2(), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._horizontalScrollTable.txt b/tdesign-component/example/assets/code/table._horizontalScrollTable.txt new file mode 100644 index 000000000..7c286f690 --- /dev/null +++ b/tdesign-component/example/assets/code/table._horizontalScrollTable.txt @@ -0,0 +1,11 @@ + + Widget _horizontalScrollTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', width: 160), + TDTableCol(title: '标题', colKey: 'title2', width: 160), + TDTableCol(title: '标题', colKey: 'title3', width: 160), + ], + data: _getData2(), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._loadingTable.txt b/tdesign-component/example/assets/code/table._loadingTable.txt new file mode 100644 index 000000000..081f3d06c --- /dev/null +++ b/tdesign-component/example/assets/code/table._loadingTable.txt @@ -0,0 +1,12 @@ + + Widget _loadingTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1'), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + loading: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._operationBtnTable.txt b/tdesign-component/example/assets/code/table._operationBtnTable.txt new file mode 100644 index 000000000..1cf435c4c --- /dev/null +++ b/tdesign-component/example/assets/code/table._operationBtnTable.txt @@ -0,0 +1,38 @@ + + Widget _operationBtnTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol( + title: '标题', + colKey: 'title4', + cellBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TDText( + '修改', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + height: 1, + ), + ), + TDText( + '通过', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + height: 1, + ), + ), + ], + ); + }, + ) + ], + data: _getData(9), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._operationIconTable.txt b/tdesign-component/example/assets/code/table._operationIconTable.txt new file mode 100644 index 000000000..55710db77 --- /dev/null +++ b/tdesign-component/example/assets/code/table._operationIconTable.txt @@ -0,0 +1,24 @@ + + Widget _operationIconTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol( + title: '标题', + colKey: 'title4', + cellBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Icon(TDIcons.upload, color: TDTheme.of(context).brandNormalColor, size: 16), + Icon(TDIcons.delete, color: TDTheme.of(context).brandNormalColor, size: 16), + ], + ); + }, + ) + ], + data: _getData(9), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._selectTable.txt b/tdesign-component/example/assets/code/table._selectTable.txt new file mode 100644 index 000000000..c75284d06 --- /dev/null +++ b/tdesign-component/example/assets/code/table._selectTable.txt @@ -0,0 +1,17 @@ + + Widget _selectTable(BuildContext context) { + return TDTable( + data: _getData(10), + columns: [ + TDTableCol(selection: true, checked: (index, row) { + return index == 0; + }, width: 50, selectable: (index, row) { + return index % 2 == 0; + }), + TDTableCol(title: '标题', colKey: 'title1'), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._sortableTable.txt b/tdesign-component/example/assets/code/table._sortableTable.txt new file mode 100644 index 000000000..45d02a01b --- /dev/null +++ b/tdesign-component/example/assets/code/table._sortableTable.txt @@ -0,0 +1,12 @@ + + Widget _sortableTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true, sortable: true), + TDTableCol(title: '标题', colKey: 'title2', sortable: true), + TDTableCol(title: '标题', colKey: 'title3', sortable: true), + TDTableCol(title: '标题', colKey: 'title4', sortable: true) + ], + data: _getData(9), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/table._stripeTable.txt b/tdesign-component/example/assets/code/table._stripeTable.txt new file mode 100644 index 000000000..72d669925 --- /dev/null +++ b/tdesign-component/example/assets/code/table._stripeTable.txt @@ -0,0 +1,13 @@ + + Widget _stripeTable(BuildContext context) { + return TDTable( + stripe: true, + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + data: _getData(9), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithContent.txt b/tdesign-component/example/assets/code/tabs._buildItemWithContent.txt new file mode 100644 index 000000000..2ac3d28cd --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithContent.txt @@ -0,0 +1,25 @@ + + Widget _buildItemWithContent(BuildContext context) { + var tabController = TabController(length: 3, vsync: this); + return SizedBox( + height: 120 + 48, + child: Column( + children: [ + TDTabBar( + tabs: subList(3), + controller: tabController, + showIndicator: true, + backgroundColor: Colors.white, + isScrollable: false,), + Container( + height: 120, + color: Colors.white, + child: TDTabBarView( + children: _getTabViews(), + controller: tabController, + ), + ) + ], + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithIcon.txt b/tdesign-component/example/assets/code/tabs._buildItemWithIcon.txt new file mode 100644 index 000000000..ddfc57bcb --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithIcon.txt @@ -0,0 +1,31 @@ + + Widget _buildItemWithIcon(BuildContext context) { + var tabs = [ + const TDTab( + text: '选项', + icon: Icon( + TDIcons.app, + size: 18, + ), + ), + const TDTab( + text: '选项', + icon: Icon( + TDIcons.app, + size: 18, + ), + ), + const TDTab( + text: '选项', + icon: Icon( + TDIcons.app, + size: 18, + ), + ), + ]; + return TDTabBar( + tabs: tabs, + controller: TabController(length: 3, vsync: this), + backgroundColor: Colors.white, + showIndicator: true); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithLogo.txt b/tdesign-component/example/assets/code/tabs._buildItemWithLogo.txt new file mode 100644 index 000000000..716aac696 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithLogo.txt @@ -0,0 +1,33 @@ + + Widget _buildItemWithLogo(BuildContext context) { + var tabs = [ + const TDTab( + text: '选项', + contentHeight: 48, + textMargin: EdgeInsets.only(right: 8), + badge: TDBadge(TDBadgeType.redPoint), + ), + const TDTab( + text: '选项', + contentHeight: 42, + textMargin: EdgeInsets.only(right: 16, top: 2, bottom: 2), + badge: TDBadge( + TDBadgeType.message, + message: '8', + ), + ), + const TDTab( + text: '选项', + height: 48, + icon: Icon( + TDIcons.app, + size: 18, + ), + ), + ]; + return TDTabBar( + tabs: tabs, + controller: TabController(length: 3, vsync: this), + backgroundColor: Colors.white, + showIndicator: true); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithOutlineCard.txt b/tdesign-component/example/assets/code/tabs._buildItemWithOutlineCard.txt new file mode 100644 index 000000000..e108db72c --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithOutlineCard.txt @@ -0,0 +1,23 @@ + + Widget _buildItemWithOutlineCard(BuildContext context) { + var tabs = [ + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + ]; + return TDTabBar( + tabs: tabs, + outlineType: TDTabBarOutlineType.card, + controller: TabController(length: 4, vsync: this), + backgroundColor: Colors.white, + showIndicator: false); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithOutlineNormal.txt b/tdesign-component/example/assets/code/tabs._buildItemWithOutlineNormal.txt new file mode 100644 index 000000000..3caf777c1 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithOutlineNormal.txt @@ -0,0 +1,23 @@ + + Widget _buildItemWithOutlineNormal(BuildContext context) { + var tabs = [ + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + ]; + return TDTabBar( + tabs: tabs, + outlineType: TDTabBarOutlineType.capsule, + controller: TabController(length: 4, vsync: this), + backgroundColor: Colors.white, + showIndicator: false); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithSizeBig.txt b/tdesign-component/example/assets/code/tabs._buildItemWithSizeBig.txt new file mode 100644 index 000000000..f68108503 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithSizeBig.txt @@ -0,0 +1,26 @@ + + Widget _buildItemWithSizeBig(BuildContext context) { + var tabs = [ + const TDTab( + text: '大尺寸', + size: TDTabSize.large, + ), + const TDTab( + text: '选项', + size: TDTabSize.large, + ), + const TDTab( + text: '选项', + size: TDTabSize.large, + ), + const TDTab( + text: '选项', + size: TDTabSize.large, + ), + ]; + return TDTabBar( + tabs: tabs, + controller: TabController(length: 4, vsync: this), + backgroundColor: Colors.white, + showIndicator: true); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithSizeSmall.txt b/tdesign-component/example/assets/code/tabs._buildItemWithSizeSmall.txt new file mode 100644 index 000000000..ae88fb384 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithSizeSmall.txt @@ -0,0 +1,22 @@ + + Widget _buildItemWithSizeSmall(BuildContext context) { + var tabs = [ + const TDTab( + text: '小尺寸', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + ]; + return TDTabBar( + tabs: tabs, + controller: TabController(length: 4, vsync: this), + backgroundColor: Colors.white, + showIndicator: true); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithSpace.txt b/tdesign-component/example/assets/code/tabs._buildItemWithSpace.txt new file mode 100644 index 000000000..446976107 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithSpace.txt @@ -0,0 +1,11 @@ + + Widget _buildItemWithSpace(BuildContext context) { + return TDTabBar( + tabs: subList(16), + controller: TabController(length: 16, vsync: this), + backgroundColor: Colors.white, + labelPadding: const EdgeInsets.all(10), + showIndicator: true, + isScrollable: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithSplit1.txt b/tdesign-component/example/assets/code/tabs._buildItemWithSplit1.txt new file mode 100644 index 000000000..9aaa5d874 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithSplit1.txt @@ -0,0 +1,9 @@ + + Widget _buildItemWithSplit1(BuildContext context) { + return TDTabBar( + tabs: subList(2), + controller: _tabController1, + backgroundColor: Colors.white, + showIndicator: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithSplit2.txt b/tdesign-component/example/assets/code/tabs._buildItemWithSplit2.txt new file mode 100644 index 000000000..2c867e056 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithSplit2.txt @@ -0,0 +1,9 @@ + + Widget _buildItemWithSplit2(BuildContext context) { + return TDTabBar( + tabs: subList(3), + controller: _tabController2, + backgroundColor: Colors.white, + showIndicator: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithSplit3.txt b/tdesign-component/example/assets/code/tabs._buildItemWithSplit3.txt new file mode 100644 index 000000000..84e6e1503 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithSplit3.txt @@ -0,0 +1,9 @@ + + Widget _buildItemWithSplit3(BuildContext context) { + return TDTabBar( + tabs: subList(4), + controller: _tabController3, + backgroundColor: Colors.white, + showIndicator: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithSplit4.txt b/tdesign-component/example/assets/code/tabs._buildItemWithSplit4.txt new file mode 100644 index 000000000..56451af74 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithSplit4.txt @@ -0,0 +1,9 @@ + + Widget _buildItemWithSplit4(BuildContext context) { + return TDTabBar( + tabs: subList(5), + controller: _tabController4, + backgroundColor: Colors.white, + showIndicator: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._buildItemWithStatus.txt b/tdesign-component/example/assets/code/tabs._buildItemWithStatus.txt new file mode 100644 index 000000000..89a437a30 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._buildItemWithStatus.txt @@ -0,0 +1,20 @@ + + Widget _buildItemWithStatus(BuildContext context) { + var tabs = [ + const TDTab( + text: '选中', + ), + const TDTab( + text: '默认', + ), + const TDTab( + text: '禁用', + enable: false, + ), + ]; + return TDTabBar( + tabs: tabs, + controller: TabController(length: 3, vsync: this), + backgroundColor: Colors.white, + showIndicator: true); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._capsuleBackgroundColor.txt b/tdesign-component/example/assets/code/tabs._capsuleBackgroundColor.txt new file mode 100644 index 000000000..1b96387bb --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._capsuleBackgroundColor.txt @@ -0,0 +1,9 @@ + + Widget _capsuleBackgroundColor(BuildContext context) { + return TDTabBar( + tabs: subList(2), + controller: _tabController1, + backgroundColor: Colors.red, + outlineType: TDTabBarOutlineType.capsule, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._customDividerStyle.txt b/tdesign-component/example/assets/code/tabs._customDividerStyle.txt new file mode 100644 index 000000000..bb98b9689 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._customDividerStyle.txt @@ -0,0 +1,11 @@ + + Widget _customDividerStyle(BuildContext context) { + return TDTabBar( + tabs: subList(2), + controller: _tabController1, + backgroundColor: Colors.white, + showIndicator: true, + dividerColor: Colors.red, + dividerHeight: 5, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._customIndicatorStyle.txt b/tdesign-component/example/assets/code/tabs._customIndicatorStyle.txt new file mode 100644 index 000000000..a641227a7 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._customIndicatorStyle.txt @@ -0,0 +1,13 @@ + + Widget _customIndicatorStyle(BuildContext context) { + return TDTabBar( + tabs: subList(2), + controller: _tabController1, + backgroundColor: Colors.white, + showIndicator: true, + indicatorColor: Colors.red, + indicatorHeight: 20, + indicatorWidth: 10, + indicatorPadding: const EdgeInsets.only(left: 20), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tabs._hideBottomDivider.txt b/tdesign-component/example/assets/code/tabs._hideBottomDivider.txt new file mode 100644 index 000000000..331cc5b55 --- /dev/null +++ b/tdesign-component/example/assets/code/tabs._hideBottomDivider.txt @@ -0,0 +1,11 @@ + + Widget _hideBottomDivider(BuildContext context) { + return TDTabBar( + tabs: subList(2), + controller: _tabController1, + backgroundColor: Colors.white, + showIndicator: true, + dividerColor: Colors.red, + dividerHeight: 0, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildAllSizeCloseTags.txt b/tdesign-component/example/assets/code/tag._buildAllSizeCloseTags.txt new file mode 100644 index 000000000..b87a0faae --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildAllSizeCloseTags.txt @@ -0,0 +1,25 @@ + + Widget _buildAllSizeCloseTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDTag( + '加大尺寸', + needCloseIcon: true, + size: TDTagSize.extraLarge, + ), + TDTag( + '大尺寸', + needCloseIcon: true, + size: TDTagSize.large, + ), + TDTag( + '中尺寸', + needCloseIcon: true, + size: TDTagSize.medium, + ), + TDTag( + '小尺寸', + needCloseIcon: true, + size: TDTagSize.small, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildAllSizeTags.txt b/tdesign-component/example/assets/code/tag._buildAllSizeTags.txt new file mode 100644 index 000000000..b2fea2a5d --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildAllSizeTags.txt @@ -0,0 +1,21 @@ + + Widget _buildAllSizeTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDTag( + '加大尺寸', + size: TDTagSize.extraLarge, + ), + TDTag( + '大尺寸', + size: TDTagSize.large, + ), + TDTag( + '中尺寸', + size: TDTagSize.medium, + ), + TDTag( + '小尺寸', + size: TDTagSize.small, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildCircleFillTag.txt b/tdesign-component/example/assets/code/tag._buildCircleFillTag.txt new file mode 100644 index 000000000..29ae171a2 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildCircleFillTag.txt @@ -0,0 +1,7 @@ + + Widget _buildCircleFillTag(BuildContext context) { + return const TDTag( + '标签文字', + shape: TDTagShape.round, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildCircleOutlineTag.txt b/tdesign-component/example/assets/code/tag._buildCircleOutlineTag.txt new file mode 100644 index 000000000..40570aeb6 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildCircleOutlineTag.txt @@ -0,0 +1,8 @@ + + Widget _buildCircleOutlineTag(BuildContext context) { + return const TDTag( + '标签文字', + shape: TDTagShape.round, + isOutline: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildCloseFillTag.txt b/tdesign-component/example/assets/code/tag._buildCloseFillTag.txt new file mode 100644 index 000000000..7e5886bad --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildCloseFillTag.txt @@ -0,0 +1,10 @@ + + Widget _buildCloseFillTag(BuildContext context) { + return TDTag( + '标签文字', + needCloseIcon: true, + onCloseTap: () { + TDToast.showText('点击关闭', context: context); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildCloseOutlineTag.txt b/tdesign-component/example/assets/code/tag._buildCloseOutlineTag.txt new file mode 100644 index 000000000..9edbc3570 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildCloseOutlineTag.txt @@ -0,0 +1,6 @@ + + Widget _buildCloseOutlineTag(BuildContext context) { + return TDTag('标签文字', needCloseIcon: true, isOutline: true, onCloseTap: () { + TDToast.showText('点击关闭', context: context); + }); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildDarkSelectTags.txt b/tdesign-component/example/assets/code/tag._buildDarkSelectTags.txt new file mode 100644 index 000000000..d441e2b81 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildDarkSelectTags.txt @@ -0,0 +1,19 @@ + + Widget _buildDarkSelectTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDSelectTag( + '未选中态', + theme: TDTagTheme.primary, + ), + TDSelectTag( + '已选中态', + theme: TDTagTheme.primary, + isSelected: true, + ), + TDSelectTag( + '不可选态', + theme: TDTagTheme.primary, + disableSelect: true, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildDarkShowTags.txt b/tdesign-component/example/assets/code/tag._buildDarkShowTags.txt new file mode 100644 index 000000000..7108aefd6 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildDarkShowTags.txt @@ -0,0 +1,25 @@ + + Widget _buildDarkShowTags(BuildContext context) { + return Wrap( + spacing: 8, + children: const [ + TDTag('默认'), + TDTag( + '主要', + theme: TDTagTheme.primary, + ), + TDTag( + '警告', + theme: TDTagTheme.warning, + ), + TDTag( + '危险', + theme: TDTagTheme.danger, + ), + TDTag( + '成功', + theme: TDTagTheme.success, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildIconFillTag.txt b/tdesign-component/example/assets/code/tag._buildIconFillTag.txt new file mode 100644 index 000000000..8558075ce --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildIconFillTag.txt @@ -0,0 +1,7 @@ + + Widget _buildIconFillTag(BuildContext context) { + return const TDTag( + '标签文字', + icon: TDIcons.discount, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildIconOutlineTag.txt b/tdesign-component/example/assets/code/tag._buildIconOutlineTag.txt new file mode 100644 index 000000000..e81babd7f --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildIconOutlineTag.txt @@ -0,0 +1,8 @@ + + Widget _buildIconOutlineTag(BuildContext context) { + return const TDTag( + '标签文字', + icon: TDIcons.discount, + isOutline: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildLightOutlineSelectTags.txt b/tdesign-component/example/assets/code/tag._buildLightOutlineSelectTags.txt new file mode 100644 index 000000000..2e4e754f3 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildLightOutlineSelectTags.txt @@ -0,0 +1,25 @@ + + Widget _buildLightOutlineSelectTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDSelectTag( + '未选中态', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + ), + TDSelectTag( + '已选中态', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + isSelected: true, + ), + TDSelectTag( + '不可选态', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + disableSelect: true, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildLightOutlineShowTags.txt b/tdesign-component/example/assets/code/tag._buildLightOutlineShowTags.txt new file mode 100644 index 000000000..5859cd486 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildLightOutlineShowTags.txt @@ -0,0 +1,33 @@ + + Widget _buildLightOutlineShowTags(BuildContext context) { + return Wrap( + spacing: 8, + children: const [ + TDTag('默认', isOutline: true, isLight: true), + TDTag( + '主要', + isOutline: true, + isLight: true, + theme: TDTagTheme.primary, + ), + TDTag( + '警告', + isOutline: true, + isLight: true, + theme: TDTagTheme.warning, + ), + TDTag( + '危险', + isOutline: true, + isLight: true, + theme: TDTagTheme.danger, + ), + TDTag( + '成功', + isOutline: true, + isLight: true, + theme: TDTagTheme.success, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildLightSelectTags.txt b/tdesign-component/example/assets/code/tag._buildLightSelectTags.txt new file mode 100644 index 000000000..33241cd00 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildLightSelectTags.txt @@ -0,0 +1,22 @@ + + Widget _buildLightSelectTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDSelectTag( + '未选中态', + theme: TDTagTheme.primary, + isLight: true, + ), + TDSelectTag( + '已选中态', + theme: TDTagTheme.primary, + isLight: true, + isSelected: true, + ), + TDSelectTag( + '不可选态', + theme: TDTagTheme.primary, + isLight: true, + disableSelect: true, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildLightShowTags.txt b/tdesign-component/example/assets/code/tag._buildLightShowTags.txt new file mode 100644 index 000000000..1a7fe996c --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildLightShowTags.txt @@ -0,0 +1,29 @@ + + Widget _buildLightShowTags(BuildContext context) { + return Wrap( + spacing: 8, + children: const [ + TDTag('默认', isLight: true), + TDTag( + '主要', + isLight: true, + theme: TDTagTheme.primary, + ), + TDTag( + '警告', + isLight: true, + theme: TDTagTheme.warning, + ), + TDTag( + '危险', + isLight: true, + theme: TDTagTheme.danger, + ), + TDTag( + '成功', + isLight: true, + theme: TDTagTheme.success, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildMarkFillTag.txt b/tdesign-component/example/assets/code/tag._buildMarkFillTag.txt new file mode 100644 index 000000000..ef7cc94c6 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildMarkFillTag.txt @@ -0,0 +1,7 @@ + + Widget _buildMarkFillTag(BuildContext context) { + return const TDTag( + '标签文字', + shape: TDTagShape.mark, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildMarkOutlineTag.txt b/tdesign-component/example/assets/code/tag._buildMarkOutlineTag.txt new file mode 100644 index 000000000..5250764bc --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildMarkOutlineTag.txt @@ -0,0 +1,8 @@ + + Widget _buildMarkOutlineTag(BuildContext context) { + return const TDTag( + '标签文字', + shape: TDTagShape.mark, + isOutline: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildOutlineSelectTags.txt b/tdesign-component/example/assets/code/tag._buildOutlineSelectTags.txt new file mode 100644 index 000000000..73785ed88 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildOutlineSelectTags.txt @@ -0,0 +1,22 @@ + + Widget _buildOutlineSelectTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDSelectTag( + '未选中态', + theme: TDTagTheme.primary, + isOutline: true, + ), + TDSelectTag( + '已选中态', + theme: TDTagTheme.primary, + isOutline: true, + isSelected: true, + ), + TDSelectTag( + '不可选态', + theme: TDTagTheme.primary, + isOutline: true, + disableSelect: true, + ), + ]); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildOutlineShowTags.txt b/tdesign-component/example/assets/code/tag._buildOutlineShowTags.txt new file mode 100644 index 000000000..b66121922 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildOutlineShowTags.txt @@ -0,0 +1,29 @@ + + Widget _buildOutlineShowTags(BuildContext context) { + return Wrap( + spacing: 8, + children: const [ + TDTag('默认', isOutline: true), + TDTag( + '主要', + isOutline: true, + theme: TDTagTheme.primary, + ), + TDTag( + '警告', + isOutline: true, + theme: TDTagTheme.warning, + ), + TDTag( + '危险', + isOutline: true, + theme: TDTagTheme.danger, + ), + TDTag( + '成功', + isOutline: true, + theme: TDTagTheme.success, + ), + ], + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildSimpleFillTag.txt b/tdesign-component/example/assets/code/tag._buildSimpleFillTag.txt new file mode 100644 index 000000000..73d83e3cc --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildSimpleFillTag.txt @@ -0,0 +1,4 @@ + + TDTag _buildSimpleFillTag(BuildContext context) { + return const TDTag('标签文字'); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tag._buildSimpleOutlineTag.txt b/tdesign-component/example/assets/code/tag._buildSimpleOutlineTag.txt new file mode 100644 index 000000000..974133b31 --- /dev/null +++ b/tdesign-component/example/assets/code/tag._buildSimpleOutlineTag.txt @@ -0,0 +1,7 @@ + + TDTag _buildSimpleOutlineTag(BuildContext context) { + return const TDTag( + '标签文字', + isOutline: true, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/text._buildCustomPaddingText.txt b/tdesign-component/example/assets/code/text._buildCustomPaddingText.txt new file mode 100644 index 000000000..fe522e003 --- /dev/null +++ b/tdesign-component/example/assets/code/text._buildCustomPaddingText.txt @@ -0,0 +1,7 @@ + + Widget _buildCustomPaddingText(BuildContext context) { + return TDTextConfiguration( + paddingConfig: CustomTextPaddingConfig(), + child: const CustomPaddingText(), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/text._buildGeneralProp.txt b/tdesign-component/example/assets/code/text._buildGeneralProp.txt new file mode 100644 index 000000000..832ce74fd --- /dev/null +++ b/tdesign-component/example/assets/code/text._buildGeneralProp.txt @@ -0,0 +1,9 @@ + + Widget _buildGeneralProp(BuildContext context) { + return TDText( + exampleTxt, + font: TDTheme.of(context).fontHeadlineLarge, + textColor: TDTheme.of(context).brandNormalColor, + backgroundColor: TDTheme.of(context).brandFocusColor, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/text._buildNormalTDText.txt b/tdesign-component/example/assets/code/text._buildNormalTDText.txt new file mode 100644 index 000000000..3206c18a6 --- /dev/null +++ b/tdesign-component/example/assets/code/text._buildNormalTDText.txt @@ -0,0 +1,6 @@ + + Widget _buildNormalTDText(BuildContext context) { + return TDText( + exampleTxt, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/text._buildRichText.txt b/tdesign-component/example/assets/code/text._buildRichText.txt new file mode 100644 index 000000000..b21e19592 --- /dev/null +++ b/tdesign-component/example/assets/code/text._buildRichText.txt @@ -0,0 +1,27 @@ + + Widget _buildRichText(BuildContext context) { + return TDText.rich( + TextSpan(children: [ + TDTextSpan( + text: 'TDTextSpan1', + font: TDTheme.of(context).fontTitleExtraLarge, + textColor: TDTheme.of(context).warningNormalColor, + isTextThrough: true, + lineThroughColor: TDTheme.of(context).brandNormalColor, + style: TextStyle(color: TDTheme.of(context).errorNormalColor)), + TextSpan( + text: 'TextSpan2', + style: TextStyle( + fontSize: 14, color: TDTheme.of(context).brandNormalColor)), + const WidgetSpan( + child: Icon( + TDIcons.setting, + size: 24, + )), + ]), + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).brandNormalColor, + style: + TextStyle(color: TDTheme.of(context).errorNormalColor, fontSize: 32), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/text._buildStyleCoverColor.txt b/tdesign-component/example/assets/code/text._buildStyleCoverColor.txt new file mode 100644 index 000000000..7cb8eeb70 --- /dev/null +++ b/tdesign-component/example/assets/code/text._buildStyleCoverColor.txt @@ -0,0 +1,9 @@ + + Widget _buildStyleCoverColor(BuildContext context) { + return TDText( + exampleTxt, + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).brandNormalColor, + style: TextStyle(color: TDTheme.of(context).errorNormalColor), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/text._buildStyleCoverColorAndFont.txt b/tdesign-component/example/assets/code/text._buildStyleCoverColorAndFont.txt new file mode 100644 index 000000000..78cd0d3d3 --- /dev/null +++ b/tdesign-component/example/assets/code/text._buildStyleCoverColorAndFont.txt @@ -0,0 +1,8 @@ + + Widget _buildStyleCoverColorAndFont(BuildContext context) { + return TDText( + exampleTxt, + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).brandNormalColor, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/text._buildSystemText.txt b/tdesign-component/example/assets/code/text._buildSystemText.txt new file mode 100644 index 000000000..5ea66aa91 --- /dev/null +++ b/tdesign-component/example/assets/code/text._buildSystemText.txt @@ -0,0 +1,6 @@ + + Widget _buildSystemText(BuildContext context) { + return Text( + exampleTxt, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/text._buildVerticalCenterText.txt b/tdesign-component/example/assets/code/text._buildVerticalCenterText.txt new file mode 100644 index 000000000..e70dd0c5d --- /dev/null +++ b/tdesign-component/example/assets/code/text._buildVerticalCenterText.txt @@ -0,0 +1,9 @@ + + Widget _buildVerticalCenterText(BuildContext context) { + return TDText( + '中华人民共和国腾讯科技', + // font: Font(size: 100, lineHeight: 100), + forceVerticalCenter: true, + backgroundColor: TDTheme.of(context).brandFocusColor, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/text._getSystemText.txt b/tdesign-component/example/assets/code/text._getSystemText.txt new file mode 100644 index 000000000..cd705f4e5 --- /dev/null +++ b/tdesign-component/example/assets/code/text._getSystemText.txt @@ -0,0 +1,7 @@ + + Widget _getSystemText(BuildContext context) { + return TDText( + exampleTxt, + backgroundColor: TDTheme.of(context).brandFocusColor, + ).getRawText(context: context); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._autoHeightType.txt b/tdesign-component/example/assets/code/textarea._autoHeightType.txt new file mode 100644 index 000000000..28d5d72fa --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._autoHeightType.txt @@ -0,0 +1,10 @@ + + Widget _autoHeightType(BuildContext context) { + return TDTextarea( + controller: controller[2], + hintText: '请输入文字', + minLines: 1, + onChanged: (value) { + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._basicType.txt b/tdesign-component/example/assets/code/textarea._basicType.txt new file mode 100644 index 000000000..4127ae9b4 --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._basicType.txt @@ -0,0 +1,12 @@ + + Widget _basicType(BuildContext context) { + return TDTextarea( + controller: controller[0], + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + onChanged: (value) { + setState(() {}); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._basicTypeByTitle.txt b/tdesign-component/example/assets/code/textarea._basicTypeByTitle.txt new file mode 100644 index 000000000..05a190304 --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._basicTypeByTitle.txt @@ -0,0 +1,12 @@ + + Widget _basicTypeByTitle(BuildContext context) { + return TDTextarea( + controller: controller[1], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + onChanged: (value) { + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._cardStyle.txt b/tdesign-component/example/assets/code/textarea._cardStyle.txt new file mode 100644 index 000000000..2521c701e --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._cardStyle.txt @@ -0,0 +1,19 @@ + + Widget _cardStyle(BuildContext context) { + return TDTextarea( + controller: controller[6], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusExtraLarge), + ), + margin: EdgeInsets.only(right: TDTheme.of(context).spacer16, left: TDTheme.of(context).spacer16), + onChanged: (value) { + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._disabledState.txt b/tdesign-component/example/assets/code/textarea._disabledState.txt new file mode 100644 index 000000000..847a16503 --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._disabledState.txt @@ -0,0 +1,14 @@ + + Widget _disabledState(BuildContext context) { + return TDTextarea( + controller: controller[4], + label: '标签文字', + hintText: '不可编辑文字', + maxLines: 4, + minLines: 4, + readOnly: true, + onChanged: (value) { + + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._extensionStyle.txt b/tdesign-component/example/assets/code/textarea._extensionStyle.txt new file mode 100644 index 000000000..3cfda14c5 --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._extensionStyle.txt @@ -0,0 +1,16 @@ + + Widget _extensionStyle(BuildContext context) { + return TDTextarea( + controller: controller[7], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + layout: TDTextareaLayout.vertical, + bordered: true, + onChanged: (value) { + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._maxLengthType.txt b/tdesign-component/example/assets/code/textarea._maxLengthType.txt new file mode 100644 index 000000000..13852a8a2 --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._maxLengthType.txt @@ -0,0 +1,14 @@ + + Widget _maxLengthType(BuildContext context) { + return TDTextarea( + controller: controller[3], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + onChanged: (value) { + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._setLabel.txt b/tdesign-component/example/assets/code/textarea._setLabel.txt new file mode 100644 index 000000000..8875fc260 --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._setLabel.txt @@ -0,0 +1,20 @@ + + Widget _setLabel(BuildContext context) { + return TDTextarea( + controller: controller[9], + label: '地址信息', + // labelWidth: 100, + labelIcon: Icon( + TDIcons.location, + size: 20, + color: TDTheme.of(context).fontGyColor1, + ), + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + onChanged: (value) { + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._setStatus.txt b/tdesign-component/example/assets/code/textarea._setStatus.txt new file mode 100644 index 000000000..70bdb90bb --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._setStatus.txt @@ -0,0 +1,17 @@ + + Widget _setStatus(BuildContext context) { + return TDTextarea( + controller: controller[10], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + layout: TDTextareaLayout.vertical, + required: true, + additionInfo: '辅助说明', + onChanged: (value) { + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._setWidth.txt b/tdesign-component/example/assets/code/textarea._setWidth.txt new file mode 100644 index 000000000..b18b76cbc --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._setWidth.txt @@ -0,0 +1,15 @@ + + Widget _setWidth(BuildContext context) { + return TDTextarea( + controller: controller[8], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + width: 200, + onChanged: (value) { + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._smallSize.txt b/tdesign-component/example/assets/code/textarea._smallSize.txt new file mode 100644 index 000000000..0204cafa8 --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._smallSize.txt @@ -0,0 +1,16 @@ + + Widget _smallSize(BuildContext context) { + return TDTextarea( + controller: controller[11], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + layout: TDTextareaLayout.vertical, + size: TDInputSize.small, + onChanged: (value) { + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/textarea._verticalStyle.txt b/tdesign-component/example/assets/code/textarea._verticalStyle.txt new file mode 100644 index 000000000..c96d3a36d --- /dev/null +++ b/tdesign-component/example/assets/code/textarea._verticalStyle.txt @@ -0,0 +1,15 @@ + + Widget _verticalStyle(BuildContext context) { + return TDTextarea( + controller: controller[5], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + layout: TDTextareaLayout.vertical, + onChanged: (value) { + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/theme._buildCustomTheme.txt b/tdesign-component/example/assets/code/theme._buildCustomTheme.txt new file mode 100644 index 000000000..7f9d1a47f --- /dev/null +++ b/tdesign-component/example/assets/code/theme._buildCustomTheme.txt @@ -0,0 +1,49 @@ + + Widget _buildCustomTheme(BuildContext context) { + /// 开启多主题 + TDTheme.needMultiTheme(true); + /// 此处替换主题 + return TDTheme( + // 替换fonts和colors,其他主题从父类拷贝 + data: TDTheme.of(context).copyWithTDThemeData('custom', fontMap: { + 'fontBodyLarge': Font(size: 40, lineHeight: 80), + }, colorMap: { + 'brandNormalColor': Colors.red + }), + // 不能直接在此处使用context,这里虽然被包裹在TGTheme中,但是context未更新,因此阿不到最新数据 + child: const TestWidget()); + + // /// 测试控件 +// class TestWidget extends StatelessWidget { +// const TestWidget({Key? key}) : super(key: key); +// +// @override +// Widget build(BuildContext context) { +// return Container( +// alignment: Alignment.center, +// child: Column( +// mainAxisSize: MainAxisSize.min, +// children: [ +// TDText( +// '使用内层赋值主题', +// font: TDTheme.of(context).fontBodyLarge, //明确使用内层主题,必须传context +// textColor: +// TDTheme.of(context).brandNormalColor, // 明确使用内层主题,必须传context +// ), +// TDText( +// '使用内层不赋值主题', +// font: TDTheme.of(context).fontTitleExtraLarge, //明确使用内层主题,必须传context +// textColor: +// TDTheme.of(context).successNormalColor, // 明确使用内层主题,必须传context +// ), +// TDText( +// '使用默认主题', +// font: TDTheme.defaultData().fontBodyLarge, //不传context,使用默认主题,此处是外层的主题 +// textColor: TDTheme.defaultData().brandNormalColor, +// ), +// ], +// ), +// ); +// } +// } + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/theme._buildDefaultTheme.txt b/tdesign-component/example/assets/code/theme._buildDefaultTheme.txt new file mode 100644 index 000000000..af2624617 --- /dev/null +++ b/tdesign-component/example/assets/code/theme._buildDefaultTheme.txt @@ -0,0 +1,19 @@ + + Widget _buildDefaultTheme(BuildContext context) { + // 通过TDTheme.of(context).xxx使用公共主题属性 + return Container( + margin: EdgeInsets.all(TDTheme.of(context).spacer8), // 间隔 + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, // 颜色 + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault), // 圆角 + boxShadow: TDTheme.of(context).shadowsBase, // 阴影 + ), + child: TDText( + '使用外层默认主题', + font: TDTheme.of(context).fontBodyLarge, // 字体,业务方使用时, + textColor: + TDTheme.of(context).brandNormalColor, // 颜色,AS中点击颜色可查看具体设置和显示效果 + ), + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildControl.txt b/tdesign-component/example/assets/code/timeCounter._buildControl.txt new file mode 100644 index 000000000..fa9fe57b2 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildControl.txt @@ -0,0 +1,60 @@ + +Widget _buildControl(BuildContext context) { + var controller = TDTimeCounterController(); + return Wrap( + direction: Axis.vertical, + spacing: 8, + children: [ + Wrap( + spacing: 8, + children: [ + TDButton( + text: '开始', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.start(); + }, + ), + TDButton( + text: '结束', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.reset(0); + }, + ), + TDButton( + text: '重置', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.reset(); + }, + ), + TDButton( + text: '暂停', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.pause(); + }, + ), + TDButton( + text: '继续', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.resume(); + }, + ), + ], + ), + TDTimeCounter( + time: 60 * 60 * 1000, + controller: controller, + // autoStart: false, + ), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildCustomNum.txt b/tdesign-component/example/assets/code/timeCounter._buildCustomNum.txt new file mode 100644 index 000000000..beed84e3a --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildCustomNum.txt @@ -0,0 +1,7 @@ + +TDTimeCounter _buildCustomNum(BuildContext context) { + return const TDTimeCounter( + time: 2000 * 60 * 1000, + format: 'mmmmmmm分sss秒', + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildCustomUnitLargeSize.txt b/tdesign-component/example/assets/code/timeCounter._buildCustomUnitLargeSize.txt new file mode 100644 index 000000000..e1189ab90 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildCustomUnitLargeSize.txt @@ -0,0 +1,10 @@ + +TDTimeCounter _buildCustomUnitLargeSize(BuildContext context) { + var style = TDTimeCounterStyle.generateStyle(context, size: TDTimeCounterSize.large); + style.timeColor = TDTheme.of(context).errorColor6; + return TDTimeCounter( + time: 60 * 60 * 1000, + splitWithUnit: true, + style: style, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildCustomUnitMediumSize.txt b/tdesign-component/example/assets/code/timeCounter._buildCustomUnitMediumSize.txt new file mode 100644 index 000000000..b8ce13c60 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildCustomUnitMediumSize.txt @@ -0,0 +1,10 @@ + +TDTimeCounter _buildCustomUnitMediumSize(BuildContext context) { + var style = TDTimeCounterStyle.generateStyle(context, size: TDTimeCounterSize.medium); + style.timeColor = TDTheme.of(context).errorColor6; + return TDTimeCounter( + time: 60 * 60 * 1000, + splitWithUnit: true, + style: style, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildCustomUnitSimple.txt b/tdesign-component/example/assets/code/timeCounter._buildCustomUnitSimple.txt new file mode 100644 index 000000000..7ec660046 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildCustomUnitSimple.txt @@ -0,0 +1,6 @@ + +TDTimeCounter _buildCustomUnitSimple(BuildContext context) { + var style = TDTimeCounterStyle.generateStyle(context); + style.timeColor = TDTheme.of(context).errorColor6; + return TDTimeCounter(time: 60 * 60 * 1000, splitWithUnit: true, style: style); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildCustomUnitSmallSize.txt b/tdesign-component/example/assets/code/timeCounter._buildCustomUnitSmallSize.txt new file mode 100644 index 000000000..f164f750d --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildCustomUnitSmallSize.txt @@ -0,0 +1,10 @@ + +TDTimeCounter _buildCustomUnitSmallSize(BuildContext context) { + var style = TDTimeCounterStyle.generateStyle(context, size: TDTimeCounterSize.small); + style.timeColor = TDTheme.of(context).errorColor6; + return TDTimeCounter( + time: 60 * 60 * 1000, + splitWithUnit: true, + style: style, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildLargeSize.txt b/tdesign-component/example/assets/code/timeCounter._buildLargeSize.txt new file mode 100644 index 000000000..7c4bcf4c8 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildLargeSize.txt @@ -0,0 +1,7 @@ + +TDTimeCounter _buildLargeSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.large, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildMediumSize.txt b/tdesign-component/example/assets/code/timeCounter._buildMediumSize.txt new file mode 100644 index 000000000..2f97c30f2 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildMediumSize.txt @@ -0,0 +1,7 @@ + +TDTimeCounter _buildMediumSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.medium, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildMillisecondSimple.txt b/tdesign-component/example/assets/code/timeCounter._buildMillisecondSimple.txt new file mode 100644 index 000000000..341aeb12e --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildMillisecondSimple.txt @@ -0,0 +1,4 @@ + +TDTimeCounter _buildMillisecondSimple(BuildContext context) { + return const TDTimeCounter(time: 60 * 60 * 1000, millisecond: true); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildRoundLargeSize.txt b/tdesign-component/example/assets/code/timeCounter._buildRoundLargeSize.txt new file mode 100644 index 000000000..768c718d3 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildRoundLargeSize.txt @@ -0,0 +1,8 @@ + +TDTimeCounter _buildRoundLargeSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.large, + theme: TDTimeCounterTheme.round, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildRoundMediumSize.txt b/tdesign-component/example/assets/code/timeCounter._buildRoundMediumSize.txt new file mode 100644 index 000000000..3bb10ef22 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildRoundMediumSize.txt @@ -0,0 +1,8 @@ + +TDTimeCounter _buildRoundMediumSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.medium, + theme: TDTimeCounterTheme.round, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildRoundSimple.txt b/tdesign-component/example/assets/code/timeCounter._buildRoundSimple.txt new file mode 100644 index 000000000..87f879b32 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildRoundSimple.txt @@ -0,0 +1,4 @@ + +TDTimeCounter _buildRoundSimple(BuildContext context) { + return const TDTimeCounter(time: 60 * 60 * 1000, theme: TDTimeCounterTheme.round); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildRoundSmallSize.txt b/tdesign-component/example/assets/code/timeCounter._buildRoundSmallSize.txt new file mode 100644 index 000000000..4bc0a95c9 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildRoundSmallSize.txt @@ -0,0 +1,8 @@ + +TDTimeCounter _buildRoundSmallSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.small, + theme: TDTimeCounterTheme.round, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildSimple.txt b/tdesign-component/example/assets/code/timeCounter._buildSimple.txt new file mode 100644 index 000000000..95273fbbd --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildSimple.txt @@ -0,0 +1,4 @@ + +TDTimeCounter _buildSimple(BuildContext context) { + return const TDTimeCounter(time: 60 * 60 * 1000); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildSmallSize.txt b/tdesign-component/example/assets/code/timeCounter._buildSmallSize.txt new file mode 100644 index 000000000..89c9ff579 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildSmallSize.txt @@ -0,0 +1,7 @@ + +TDTimeCounter _buildSmallSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.small, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildSquareLargeSize.txt b/tdesign-component/example/assets/code/timeCounter._buildSquareLargeSize.txt new file mode 100644 index 000000000..ab190a089 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildSquareLargeSize.txt @@ -0,0 +1,8 @@ + +TDTimeCounter _buildSquareLargeSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.large, + theme: TDTimeCounterTheme.square, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildSquareMediumSize.txt b/tdesign-component/example/assets/code/timeCounter._buildSquareMediumSize.txt new file mode 100644 index 000000000..b484e694b --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildSquareMediumSize.txt @@ -0,0 +1,8 @@ + +TDTimeCounter _buildSquareMediumSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.medium, + theme: TDTimeCounterTheme.square, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildSquareSimple.txt b/tdesign-component/example/assets/code/timeCounter._buildSquareSimple.txt new file mode 100644 index 000000000..fbdabeb7a --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildSquareSimple.txt @@ -0,0 +1,4 @@ + +TDTimeCounter _buildSquareSimple(BuildContext context) { + return const TDTimeCounter(time: 60 * 60 * 1000, theme: TDTimeCounterTheme.square); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildSquareSmallSize.txt b/tdesign-component/example/assets/code/timeCounter._buildSquareSmallSize.txt new file mode 100644 index 000000000..b71918c80 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildSquareSmallSize.txt @@ -0,0 +1,8 @@ + +TDTimeCounter _buildSquareSmallSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.small, + theme: TDTimeCounterTheme.square, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildUnitLargeSize.txt b/tdesign-component/example/assets/code/timeCounter._buildUnitLargeSize.txt new file mode 100644 index 000000000..9ae518ea0 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildUnitLargeSize.txt @@ -0,0 +1,9 @@ + +TDTimeCounter _buildUnitLargeSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.large, + theme: TDTimeCounterTheme.square, + splitWithUnit: true, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildUnitMediumSize.txt b/tdesign-component/example/assets/code/timeCounter._buildUnitMediumSize.txt new file mode 100644 index 000000000..04f480130 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildUnitMediumSize.txt @@ -0,0 +1,9 @@ + +TDTimeCounter _buildUnitMediumSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.medium, + theme: TDTimeCounterTheme.square, + splitWithUnit: true, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildUnitSimple.txt b/tdesign-component/example/assets/code/timeCounter._buildUnitSimple.txt new file mode 100644 index 000000000..cab7f72cd --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildUnitSimple.txt @@ -0,0 +1,4 @@ + +TDTimeCounter _buildUnitSimple(BuildContext context) { + return const TDTimeCounter(time: 60 * 60 * 1000, theme: TDTimeCounterTheme.square, splitWithUnit: true); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildUnitSmallSize.txt b/tdesign-component/example/assets/code/timeCounter._buildUnitSmallSize.txt new file mode 100644 index 000000000..c762a3c5f --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildUnitSmallSize.txt @@ -0,0 +1,9 @@ + +TDTimeCounter _buildUnitSmallSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.small, + theme: TDTimeCounterTheme.square, + splitWithUnit: true, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/timeCounter._buildUpSimple.txt b/tdesign-component/example/assets/code/timeCounter._buildUpSimple.txt new file mode 100644 index 000000000..a090d55a1 --- /dev/null +++ b/tdesign-component/example/assets/code/timeCounter._buildUpSimple.txt @@ -0,0 +1,8 @@ + +TDTimeCounter _buildUpSimple(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + millisecond: true, + direction: TDTimeCounterDirection.up, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._customMultipleToast.txt b/tdesign-component/example/assets/code/toast._customMultipleToast.txt new file mode 100644 index 000000000..612fb1acc --- /dev/null +++ b/tdesign-component/example/assets/code/toast._customMultipleToast.txt @@ -0,0 +1,17 @@ + + Widget _customMultipleToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showText( + '最多一行展示十个汉字宽度限制最多不超过三行文字最多一行展示十个汉字宽度限制最多不超过三行文字最多一行展示十个汉字宽度限制最多不超过三行文字最多一行展示十个汉字宽度限制最多不超过三行文字最多一行展示十个汉字宽度限制最多不超过三行文字最多一行展示十个汉字宽度限制最多不超过三行文字', + context: context, + constraints: BoxConstraints(maxWidth: 350.scale), + maxLines: 5); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '多行文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._dismissLoadingToast.txt b/tdesign-component/example/assets/code/toast._dismissLoadingToast.txt new file mode 100644 index 000000000..eea11f9e2 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._dismissLoadingToast.txt @@ -0,0 +1,11 @@ + + Widget _dismissLoadingToast(BuildContext context) { + return const TDButton( + onTap: TDToast.dismissLoading, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '停止加载', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._failToast.txt b/tdesign-component/example/assets/code/toast._failToast.txt new file mode 100644 index 000000000..b3ccec860 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._failToast.txt @@ -0,0 +1,13 @@ + + Widget _failToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showFail('失败文案', direction: IconTextDirection.horizontal, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '失败提示', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._failVerticalToast.txt b/tdesign-component/example/assets/code/toast._failVerticalToast.txt new file mode 100644 index 000000000..d82c7f322 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._failVerticalToast.txt @@ -0,0 +1,13 @@ + + Widget _failVerticalToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showFail('失败文案', direction: IconTextDirection.vertical, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '失败提示(竖向)', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._horizontalIconToast.txt b/tdesign-component/example/assets/code/toast._horizontalIconToast.txt new file mode 100644 index 000000000..c21748201 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._horizontalIconToast.txt @@ -0,0 +1,13 @@ + + Widget _horizontalIconToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showIconText('带横向图标', icon: TDIcons.check_circle, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '带横向图标', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._loadingCustomToast.txt b/tdesign-component/example/assets/code/toast._loadingCustomToast.txt new file mode 100644 index 000000000..7241ae365 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._loadingCustomToast.txt @@ -0,0 +1,18 @@ + + Widget _loadingCustomToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showLoading(context: context, customWidget: Container( + width: 50, + height: 20, + child: const TDText('自定义加载'), + color: TDTheme.of(context).brandColor1, + )); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '加载状态', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._loadingToast.txt b/tdesign-component/example/assets/code/toast._loadingToast.txt new file mode 100644 index 000000000..76537cea1 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._loadingToast.txt @@ -0,0 +1,13 @@ + + Widget _loadingToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showLoading(context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '加载状态', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._loadingWithoutTextToast.txt b/tdesign-component/example/assets/code/toast._loadingWithoutTextToast.txt new file mode 100644 index 000000000..c3fe38337 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._loadingWithoutTextToast.txt @@ -0,0 +1,13 @@ + + Widget _loadingWithoutTextToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showLoadingWithoutText(context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '加载状态(无文案)', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._multipleToast.txt b/tdesign-component/example/assets/code/toast._multipleToast.txt new file mode 100644 index 000000000..67ad8500c --- /dev/null +++ b/tdesign-component/example/assets/code/toast._multipleToast.txt @@ -0,0 +1,13 @@ + + Widget _multipleToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showText('最多一行展示十个汉字宽度限制最多不超过三行文字', context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '多行文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._preventTapToast.txt b/tdesign-component/example/assets/code/toast._preventTapToast.txt new file mode 100644 index 000000000..8fdf5339c --- /dev/null +++ b/tdesign-component/example/assets/code/toast._preventTapToast.txt @@ -0,0 +1,13 @@ + + Widget _preventTapToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showText('轻提示文字内容', context: context, preventTap: true, backgroundColor: Colors.black.withOpacity(0.7)); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '禁止滚动+点击', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._successToast.txt b/tdesign-component/example/assets/code/toast._successToast.txt new file mode 100644 index 000000000..0eb1278b0 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._successToast.txt @@ -0,0 +1,13 @@ + + Widget _successToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showSuccess('成功文案', context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '成功提示', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._successVerticalToast.txt b/tdesign-component/example/assets/code/toast._successVerticalToast.txt new file mode 100644 index 000000000..998cdc0f6 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._successVerticalToast.txt @@ -0,0 +1,13 @@ + + Widget _successVerticalToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showSuccess('成功文案', direction: IconTextDirection.vertical, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '成功提示(竖向)', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._textCustomToast.txt b/tdesign-component/example/assets/code/toast._textCustomToast.txt new file mode 100644 index 000000000..60c615cca --- /dev/null +++ b/tdesign-component/example/assets/code/toast._textCustomToast.txt @@ -0,0 +1,20 @@ + + Widget _textCustomToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showText('自定义纯文字', + context: context, + customWidget: Container( + width: 50, + height: 20, + child: const TDText('自定义纯文字'), + color: TDTheme.of(context).brandClickColor, + )); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '纯文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._textToast.txt b/tdesign-component/example/assets/code/toast._textToast.txt new file mode 100644 index 000000000..1cf9f6b3f --- /dev/null +++ b/tdesign-component/example/assets/code/toast._textToast.txt @@ -0,0 +1,13 @@ + + Widget _textToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showText('轻提示文字内容', context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '纯文字', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._verticalIconToast.txt b/tdesign-component/example/assets/code/toast._verticalIconToast.txt new file mode 100644 index 000000000..8feff16c1 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._verticalIconToast.txt @@ -0,0 +1,14 @@ + + Widget _verticalIconToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showIconText('带竖向图标', + icon: TDIcons.check_circle, direction: IconTextDirection.vertical, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '带竖向图标', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._warningToast.txt b/tdesign-component/example/assets/code/toast._warningToast.txt new file mode 100644 index 000000000..652941ea5 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._warningToast.txt @@ -0,0 +1,13 @@ + + Widget _warningToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showWarning('警告文案', direction: IconTextDirection.horizontal, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '警告提示', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/toast._warningVerticalToast.txt b/tdesign-component/example/assets/code/toast._warningVerticalToast.txt new file mode 100644 index 000000000..80d853a51 --- /dev/null +++ b/tdesign-component/example/assets/code/toast._warningVerticalToast.txt @@ -0,0 +1,13 @@ + + Widget _warningVerticalToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showWarning('警告文案', direction: IconTextDirection.vertical, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '警告提示(竖向)', + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tree._buildDefaultTreeSelect.txt b/tdesign-component/example/assets/code/tree._buildDefaultTreeSelect.txt new file mode 100644 index 000000000..7f65022e8 --- /dev/null +++ b/tdesign-component/example/assets/code/tree._buildDefaultTreeSelect.txt @@ -0,0 +1,24 @@ + + Widget _buildDefaultTreeSelect(BuildContext context) { + var options = []; + + for (var i = 1; i <= 10; i++) { + options.add(TDSelectOption(label: '选项$i', value: i, children: [])); + + for (var j = 1; j <= 10; j++) { + options[i - 1].children.add(TDSelectOption( + label: '选项$i.$j', + value: i * 10 + j, + children: [], + )); + } + } + + return TDTreeSelect( + options: options, + defaultValue: values1, + onChange: (val, level) { + print('$val, $level'); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tree._buildMultipleTreeSelect.txt b/tdesign-component/example/assets/code/tree._buildMultipleTreeSelect.txt new file mode 100644 index 000000000..781a8dc55 --- /dev/null +++ b/tdesign-component/example/assets/code/tree._buildMultipleTreeSelect.txt @@ -0,0 +1,22 @@ + + Widget _buildMultipleTreeSelect(BuildContext context) { + var options = []; + + for (var i = 1; i <= 10; i++) { + options.add(TDSelectOption(label: '选项$i', value: i, children: [])); + + for (var j = 1; j <= 10; j++) { + options[i - 1].children.add( + TDSelectOption(label: '选项$i.$j', value: i * 10 + j, children: [])); + } + } + + return TDTreeSelect( + options: options, + defaultValue: values2, + multiple: true, + onChange: (val, level) { + print('$val, $level'); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tree._buildPartMultipleTreeSelect.txt b/tdesign-component/example/assets/code/tree._buildPartMultipleTreeSelect.txt new file mode 100644 index 000000000..70b5131fe --- /dev/null +++ b/tdesign-component/example/assets/code/tree._buildPartMultipleTreeSelect.txt @@ -0,0 +1,25 @@ + + Widget _buildPartMultipleTreeSelect(BuildContext context) { + var options = []; + + for (var i = 1; i <= 2; i++) { + options.add(TDSelectOption( + label: '${i == 1 ? '单选' : '多选'}', value: i, children: [])); + + for (var j = 1; j <= 10; j++) { + options[i - 1].children.add(TDSelectOption( + label: '选项$i.$j', + value: i * 10 + j, + children: [], + multiple: i == 2)); + } + } + + return TDTreeSelect( + options: options, + defaultValue: values1, + onChange: (val, level) { + print('$val, $level'); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/tree._buildThirdTreeSelect.txt b/tdesign-component/example/assets/code/tree._buildThirdTreeSelect.txt new file mode 100644 index 000000000..92a80baf9 --- /dev/null +++ b/tdesign-component/example/assets/code/tree._buildThirdTreeSelect.txt @@ -0,0 +1,26 @@ + + Widget _buildThirdTreeSelect(BuildContext context) { + var options = []; + + for (var i = 1; i <= 10; i++) { + options.add(TDSelectOption(label: '选项$i', value: i, children: [])); + + for (var j = 1; j <= 10; j++) { + options[i - 1].children.add( + TDSelectOption(label: '选项$i.$j', value: i * 10 + j, children: [])); + + for (var k = 1; k <= 10; k++) { + options[i - 1].children[j - 1].children.add( + TDSelectOption(label: '选项$i.$j.$k', value: i * 100 + j * 10 + k)); + } + } + } + + return TDTreeSelect( + options: options, + defaultValue: values3, + onChange: (val, level) { + print('$val, $level'); + }, + ); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/upload._uploadError.txt b/tdesign-component/example/assets/code/upload._uploadError.txt new file mode 100644 index 000000000..67d199829 --- /dev/null +++ b/tdesign-component/example/assets/code/upload._uploadError.txt @@ -0,0 +1,14 @@ + + Widget _uploadError(BuildContext context) { + return wrapDemoContainer('上传图片', + child: TDUpload( + files: files5, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files5, files, type)), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/upload._uploadLoading.txt b/tdesign-component/example/assets/code/upload._uploadLoading.txt new file mode 100644 index 000000000..f65d05c7b --- /dev/null +++ b/tdesign-component/example/assets/code/upload._uploadLoading.txt @@ -0,0 +1,14 @@ + + Widget _uploadLoading(BuildContext context) { + return wrapDemoContainer('上传图片', + child: TDUpload( + files: files3, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files3, files, type)), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/upload._uploadMultiple.txt b/tdesign-component/example/assets/code/upload._uploadMultiple.txt new file mode 100644 index 000000000..d0cfac435 --- /dev/null +++ b/tdesign-component/example/assets/code/upload._uploadMultiple.txt @@ -0,0 +1,14 @@ + + Widget _uploadMultiple(BuildContext context) { + return wrapDemoContainer('多选上传', + child: TDUpload( + files: files2, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files2, files, type)), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/upload._uploadRetry.txt b/tdesign-component/example/assets/code/upload._uploadRetry.txt new file mode 100644 index 000000000..9ac8cfa58 --- /dev/null +++ b/tdesign-component/example/assets/code/upload._uploadRetry.txt @@ -0,0 +1,14 @@ + + Widget _uploadRetry(BuildContext context) { + return wrapDemoContainer('上传图片', + child: TDUpload( + files: files4, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files4, files, type)), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/upload._uploadSingle.txt b/tdesign-component/example/assets/code/upload._uploadSingle.txt new file mode 100644 index 000000000..ba83a8309 --- /dev/null +++ b/tdesign-component/example/assets/code/upload._uploadSingle.txt @@ -0,0 +1,12 @@ + + Widget _uploadSingle(BuildContext context) { + return wrapDemoContainer('单选上传', + child: TDUpload( + files: files1, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files1, files, type)), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/upload._uploadSingleWithReplace.txt b/tdesign-component/example/assets/code/upload._uploadSingleWithReplace.txt new file mode 100644 index 000000000..e1257e72e --- /dev/null +++ b/tdesign-component/example/assets/code/upload._uploadSingleWithReplace.txt @@ -0,0 +1,16 @@ + + Widget _uploadSingleWithReplace(BuildContext context) { + return wrapDemoContainer('单选上传(替换)', + child: TDUpload( + files: files6, + width: 60, + height: 60, + type: TDUploadBoxType.circle, + enabledReplaceType: true, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files6, files, type)), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/code/upload._uploadSizeLimit.txt b/tdesign-component/example/assets/code/upload._uploadSizeLimit.txt new file mode 100644 index 000000000..7e7900c56 --- /dev/null +++ b/tdesign-component/example/assets/code/upload._uploadSizeLimit.txt @@ -0,0 +1,13 @@ + + Widget _uploadSizeLimit(BuildContext context) { + return wrapDemoContainer('限制10KB', + child: TDUpload( + files: files1, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + sizeLimit: 10, + onChange: ((files, type) => onValueChanged(files1, files, type)), + )); + } \ No newline at end of file diff --git a/tdesign-component/example/assets/img/empty.png b/tdesign-component/example/assets/img/empty.png new file mode 100644 index 000000000..70d00ae06 Binary files /dev/null and b/tdesign-component/example/assets/img/empty.png differ diff --git a/tdesign-component/example/assets/img/illustration.png b/tdesign-component/example/assets/img/illustration.png new file mode 100644 index 000000000..fee3ad5f9 Binary files /dev/null and b/tdesign-component/example/assets/img/illustration.png differ diff --git a/tdesign-component/example/assets/img/image.png b/tdesign-component/example/assets/img/image.png new file mode 100644 index 000000000..3a2cccede Binary files /dev/null and b/tdesign-component/example/assets/img/image.png differ diff --git a/tdesign-component/example/assets/img/td_action_sheet_1.png b/tdesign-component/example/assets/img/td_action_sheet_1.png new file mode 100644 index 000000000..8043ecdc3 Binary files /dev/null and b/tdesign-component/example/assets/img/td_action_sheet_1.png differ diff --git a/tdesign-component/example/assets/img/td_action_sheet_2.png b/tdesign-component/example/assets/img/td_action_sheet_2.png new file mode 100644 index 000000000..021c31fdb Binary files /dev/null and b/tdesign-component/example/assets/img/td_action_sheet_2.png differ diff --git a/tdesign-component/example/assets/img/td_action_sheet_3.png b/tdesign-component/example/assets/img/td_action_sheet_3.png new file mode 100644 index 000000000..4b84dc3c2 Binary files /dev/null and b/tdesign-component/example/assets/img/td_action_sheet_3.png differ diff --git a/tdesign-component/example/assets/img/td_action_sheet_4.png b/tdesign-component/example/assets/img/td_action_sheet_4.png new file mode 100644 index 000000000..ce29797b6 Binary files /dev/null and b/tdesign-component/example/assets/img/td_action_sheet_4.png differ diff --git a/tdesign-component/example/assets/img/td_action_sheet_5.png b/tdesign-component/example/assets/img/td_action_sheet_5.png new file mode 100644 index 000000000..05fea25de Binary files /dev/null and b/tdesign-component/example/assets/img/td_action_sheet_5.png differ diff --git a/tdesign-component/example/assets/img/td_action_sheet_6.png b/tdesign-component/example/assets/img/td_action_sheet_6.png new file mode 100644 index 000000000..8146cc717 Binary files /dev/null and b/tdesign-component/example/assets/img/td_action_sheet_6.png differ diff --git a/tdesign-component/example/assets/img/td_action_sheet_7.png b/tdesign-component/example/assets/img/td_action_sheet_7.png new file mode 100644 index 000000000..6ed40bcfc Binary files /dev/null and b/tdesign-component/example/assets/img/td_action_sheet_7.png differ diff --git a/tdesign-component/example/assets/img/td_action_sheet_8.png b/tdesign-component/example/assets/img/td_action_sheet_8.png new file mode 100644 index 000000000..76b30b8f5 Binary files /dev/null and b/tdesign-component/example/assets/img/td_action_sheet_8.png differ diff --git a/tdesign-component/example/assets/img/td_avatar_1.png b/tdesign-component/example/assets/img/td_avatar_1.png new file mode 100644 index 000000000..fb9f230f3 Binary files /dev/null and b/tdesign-component/example/assets/img/td_avatar_1.png differ diff --git a/tdesign-component/example/assets/img/td_avatar_2.png b/tdesign-component/example/assets/img/td_avatar_2.png new file mode 100644 index 000000000..cff4d8e8d Binary files /dev/null and b/tdesign-component/example/assets/img/td_avatar_2.png differ diff --git a/tdesign-component/example/assets/img/td_brand.png b/tdesign-component/example/assets/img/td_brand.png new file mode 100644 index 000000000..93a0b0884 Binary files /dev/null and b/tdesign-component/example/assets/img/td_brand.png differ diff --git a/tdesign-component/example/assets/publish_time b/tdesign-component/example/assets/publish_time new file mode 100644 index 000000000..49520a5ae --- /dev/null +++ b/tdesign-component/example/assets/publish_time @@ -0,0 +1 @@ +1749635071000 diff --git a/tdesign-component/example/assets/theme.json b/tdesign-component/example/assets/theme.json new file mode 100644 index 000000000..88dfcec04 --- /dev/null +++ b/tdesign-component/example/assets/theme.json @@ -0,0 +1,178 @@ +{ + "green": { + "ref": { + "brandLightColor": "brandColor1", + "brandFocusColor": "brandColor2", + "brandDisabledColor": "brandColor3", + "brandHoverColor": "brandColor3", + "brandNormalColor": "brandColor4", + "brandActiveColor": "brandColor5", + "warningNormalColor": "warningColor5", + "warningHoverColor": "warningColor4", + "warningFocusColor": "warningColor2", + "warningActiveColor": "warningColor6", + "warningDisabledColor": "warningColor3", + "warningLightColor": "warningColor1", + "errorNormalColor": "errorColor6", + "errorHoverColor": "errorColor5", + "errorFocusColor": "errorColor2", + "errorActiveColor": "errorColor7", + "errorDisabledColor": "errorColor3", + "errorLightColor": "errorColor1", + "successNormalColor": "successColor5", + "successHoverColor": "successColor4", + "successFocusColor": "successColor2", + "successActiveColor": "successColor6", + "successDisabledColor": "successColor3", + "successLightColor": "successColor1", + "brandColorLightHover": "brandColor2", + "warningColorLightHover": "warningColor2", + "errorColorLightHover": "errorColor2", + "successColorLightHover": "successColor2" + }, + "color": { + "brandColor1": "#e4f9e9", + "brandColor2": "#c8f2d7", + "brandColor3": "#94dab2", + "brandColor4": "#45c58b", + "brandColor5": "#33a371", + "brandColor6": "#008857", + "brandColor7": "#006c44", + "brandColor8": "#005333", + "brandColor9": "#003b23", + "brandColor10": "#002515", + "warningColor1": "#fef3e6", + "warningColor2": "#f9e0c7", + "warningColor3": "#f7c797", + "warningColor4": "#f2995f", + "warningColor5": "#ed7b2f", + "warningColor6": "#d35a21", + "warningColor7": "#ba431b", + "warningColor8": "#9e3610", + "warningColor9": "#842b0b", + "warningColor10": "#5a1907", + "errorColor1": "#fdecee", + "errorColor2": "#f9d7d9", + "errorColor3": "#f8b9be", + "errorColor4": "#f78d94", + "errorColor5": "#f36d78", + "errorColor6": "#e34d59", + "errorColor7": "#c9353f", + "errorColor8": "#b11f26", + "errorColor9": "#951114", + "errorColor10": "#680506", + "successColor1": "#e8f8f2", + "successColor2": "#bcebdc", + "successColor3": "#85dbbe", + "successColor4": "#48c79c", + "successColor5": "#00a870", + "successColor6": "#078d5c", + "successColor7": "#067945", + "successColor8": "#056334", + "successColor9": "#044f2a", + "successColor10": "#033017", + "grayColor1": "#f3f3f3", + "grayColor2": "#eeeeee", + "grayColor3": "#e7e7e7", + "grayColor4": "#dcdcdc", + "grayColor5": "#c5c5c5", + "grayColor6": "#a6a6a6", + "grayColor7": "#8b8b8b", + "grayColor8": "#777777", + "grayColor9": "#5e5e5e", + "grayColor10": "#4b4b4b", + "grayColor11": "#383838", + "grayColor12": "#2c2c2c", + "grayColor13": "#242424", + "grayColor14": "#181818" + } + }, + "red": { + "ref": { + "brandLightColor": "brandColor1", + "brandFocusColor": "brandColor2", + "brandDisabledColor": "brandColor3", + "brandHoverColor": "brandColor4", + "brandNormalColor": "brandColor5", + "brandActiveColor": "brandColor6", + "warningNormalColor": "warningColor5", + "warningHoverColor": "warningColor4", + "warningFocusColor": "warningColor2", + "warningActiveColor": "warningColor6", + "warningDisabledColor": "warningColor3", + "warningLightColor": "warningColor1", + "errorNormalColor": "errorColor6", + "errorHoverColor": "errorColor5", + "errorFocusColor": "errorColor2", + "errorActiveColor": "errorColor7", + "errorDisabledColor": "errorColor3", + "errorLightColor": "errorColor1", + "successNormalColor": "successColor5", + "successHoverColor": "successColor4", + "successFocusColor": "successColor2", + "successActiveColor": "successColor6", + "successDisabledColor": "successColor3", + "successLightColor": "successColor1", + "brandColorLightHover": "brandColor2", + "warningColorLightHover": "warningColor2", + "errorColorLightHover": "errorColor2", + "successColorLightHover": "successColor2" + }, + "color": { + "brandColor1": "#fff0f1", + "brandColor2": "#ffd8dd", + "brandColor3": "#ffb7c1", + "brandColor4": "#ff8fa2", + "brandColor5": "#ff5479", + "brandColor6": "#db3d62", + "brandColor7": "#b2294b", + "brandColor8": "#8d1135", + "brandColor9": "#690021", + "brandColor10": "#480014", + "warningColor1": "#fef3e6", + "warningColor2": "#f9e0c7", + "warningColor3": "#f7c797", + "warningColor4": "#f2995f", + "warningColor5": "#ed7b2f", + "warningColor6": "#d35a21", + "warningColor7": "#ba431b", + "warningColor8": "#9e3610", + "warningColor9": "#842b0b", + "warningColor10": "#5a1907", + "errorColor1": "#fdecee", + "errorColor2": "#f9d7d9", + "errorColor3": "#f8b9be", + "errorColor4": "#f78d94", + "errorColor5": "#f36d78", + "errorColor6": "#e34d59", + "errorColor7": "#c9353f", + "errorColor8": "#b11f26", + "errorColor9": "#951114", + "errorColor10": "#680506", + "successColor1": "#e8f8f2", + "successColor2": "#bcebdc", + "successColor3": "#85dbbe", + "successColor4": "#48c79c", + "successColor5": "#00a870", + "successColor6": "#078d5c", + "successColor7": "#067945", + "successColor8": "#056334", + "successColor9": "#044f2a", + "successColor10": "#033017", + "grayColor1": "#f3f3f3", + "grayColor2": "#eeeeee", + "grayColor3": "#e7e7e7", + "grayColor4": "#dcdcdc", + "grayColor5": "#c5c5c5", + "grayColor6": "#a6a6a6", + "grayColor7": "#8b8b8b", + "grayColor8": "#777777", + "grayColor9": "#5e5e5e", + "grayColor10": "#4b4b4b", + "grayColor11": "#383838", + "grayColor12": "#2c2c2c", + "grayColor13": "#242424", + "grayColor14": "#181818" + } + } +} \ No newline at end of file diff --git a/tdesign-component/example/assets/version b/tdesign-component/example/assets/version new file mode 100644 index 000000000..10196b49b --- /dev/null +++ b/tdesign-component/example/assets/version @@ -0,0 +1 @@ +0.0.0.2 \ No newline at end of file diff --git a/example/ios/.gitignore b/tdesign-component/example/ios/.gitignore similarity index 100% rename from example/ios/.gitignore rename to tdesign-component/example/ios/.gitignore diff --git a/example/ios/ExportOptionsDemoPlist.plist b/tdesign-component/example/ios/ExportOptionsDemoPlist.plist similarity index 93% rename from example/ios/ExportOptionsDemoPlist.plist rename to tdesign-component/example/ios/ExportOptionsDemoPlist.plist index 2acecb21a..f9be6665c 100644 --- a/example/ios/ExportOptionsDemoPlist.plist +++ b/tdesign-component/example/ios/ExportOptionsDemoPlist.plist @@ -11,7 +11,7 @@ provisioningProfiles com.tencent.tdesign.db - * InHouse + com.tencent.tdesign.db InHouse signingCertificate iPhone Distribution: Tencent Technology (Shenzhen) Co., Ltd diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/tdesign-component/example/ios/Flutter/AppFrameworkInfo.plist similarity index 96% rename from example/ios/Flutter/AppFrameworkInfo.plist rename to tdesign-component/example/ios/Flutter/AppFrameworkInfo.plist index 8d4492f97..7c5696400 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/tdesign-component/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 12.0 diff --git a/tdesign-component/example/ios/Flutter/Debug.xcconfig b/tdesign-component/example/ios/Flutter/Debug.xcconfig new file mode 100644 index 000000000..96e908558 --- /dev/null +++ b/tdesign-component/example/ios/Flutter/Debug.xcconfig @@ -0,0 +1,3 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/tdesign-component/example/ios/Flutter/Release.xcconfig b/tdesign-component/example/ios/Flutter/Release.xcconfig new file mode 100644 index 000000000..388e6ffcd --- /dev/null +++ b/tdesign-component/example/ios/Flutter/Release.xcconfig @@ -0,0 +1,3 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/example/ios/Podfile b/tdesign-component/example/ios/Podfile similarity index 98% rename from example/ios/Podfile rename to tdesign-component/example/ios/Podfile index f7d6a5e68..b331c7b2b 100644 --- a/example/ios/Podfile +++ b/tdesign-component/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/tdesign-component/example/ios/Podfile.lock b/tdesign-component/example/ios/Podfile.lock new file mode 100644 index 000000000..4c9913399 --- /dev/null +++ b/tdesign-component/example/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - Flutter (1.0.0) + - image_picker_ios (0.0.1): + - Flutter + +DEPENDENCIES: + - Flutter (from `Flutter`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: Flutter + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" + +SPEC CHECKSUMS: + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 + +PODFILE CHECKSUM: cf0c950f7e9a456b4e325f5b8fc0f98906a3705a + +COCOAPODS: 1.16.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/tdesign-component/example/ios/Runner.xcodeproj/project.pbxproj similarity index 91% rename from example/ios/Runner.xcodeproj/project.pbxproj rename to tdesign-component/example/ios/Runner.xcodeproj/project.pbxproj index a5f9b17cf..a00363849 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/tdesign-component/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -152,6 +152,7 @@ 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + BDA91D8CAA844B388F3140CD /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -168,7 +169,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -234,10 +235,12 @@ }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -248,6 +251,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -260,6 +264,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + BDA91D8CAA844B388F3140CD /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -336,7 +357,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -350,10 +371,12 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = HKB8WPFBR9; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKB8WPFBR9; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -362,7 +385,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.tencent.tdesign.db; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "* InHouse"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "com.tencent.tdesign.db InHouse"; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -414,7 +438,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -463,7 +487,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -477,10 +501,12 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = HKB8WPFBR9; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKB8WPFBR9; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -489,7 +515,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.tencent.tdesign.db; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "* InHouse"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "com.tencent.tdesign.db InHouse"; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -499,10 +526,12 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "iPhone Distribution"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = HKB8WPFBR9; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKB8WPFBR9; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -511,7 +540,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.tencent.tdesign.db; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = "* InHouse"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "com.tencent.tdesign.db InHouse"; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/tdesign-component/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to tdesign-component/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/tdesign-component/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to tdesign-component/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/tdesign-component/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to tdesign-component/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/tdesign-component/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 93% rename from example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to tdesign-component/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a33..9c12df59c 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/tdesign-component/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/tdesign-component/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to tdesign-component/example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/tdesign-component/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to tdesign-component/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/tdesign-component/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to tdesign-component/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner/AppDelegate.h b/tdesign-component/example/ios/Runner/AppDelegate.h similarity index 100% rename from example/ios/Runner/AppDelegate.h rename to tdesign-component/example/ios/Runner/AppDelegate.h diff --git a/example/ios/Runner/AppDelegate.m b/tdesign-component/example/ios/Runner/AppDelegate.m similarity index 100% rename from example/ios/Runner/AppDelegate.m rename to tdesign-component/example/ios/Runner/AppDelegate.m diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/tdesign-component/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to tdesign-component/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/tdesign-component/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/tdesign-component/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to tdesign-component/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/tdesign-component/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to tdesign-component/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/tdesign-component/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to tdesign-component/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/tdesign-component/example/ios/Runner/Base.lproj/Main.storyboard similarity index 97% rename from example/ios/Runner/Base.lproj/Main.storyboard rename to tdesign-component/example/ios/Runner/Base.lproj/Main.storyboard index f3c28516f..9caf036fb 100644 --- a/example/ios/Runner/Base.lproj/Main.storyboard +++ b/tdesign-component/example/ios/Runner/Base.lproj/Main.storyboard @@ -6,7 +6,7 @@ - + diff --git a/example/ios/Runner/Info.plist b/tdesign-component/example/ios/Runner/Info.plist similarity index 81% rename from example/ios/Runner/Info.plist rename to tdesign-component/example/ios/Runner/Info.plist index 40f64a48c..55b3bfc10 100644 --- a/example/ios/Runner/Info.plist +++ b/tdesign-component/example/ios/Runner/Info.plist @@ -18,6 +18,12 @@ APPL CFBundleShortVersionString $(FLUTTER_BUILD_NAME) + NSCameraUsageDescription + UploadComponentNeeded + NSMicrophoneUsageDescription + UploadComponentNeeded + NSPhotoLibraryUsageDescription + UploadComponentNeeded CFBundleSignature ???? CFBundleVersion @@ -43,5 +49,9 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/example/ios/Runner/main.m b/tdesign-component/example/ios/Runner/main.m similarity index 100% rename from example/ios/Runner/main.m rename to tdesign-component/example/ios/Runner/main.m diff --git a/tdesign-component/example/l10n.yaml b/tdesign-component/example/l10n.yaml new file mode 100644 index 000000000..4e6692e55 --- /dev/null +++ b/tdesign-component/example/l10n.yaml @@ -0,0 +1,3 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart \ No newline at end of file diff --git a/example/lib/about.dart b/tdesign-component/example/lib/about.dart similarity index 75% rename from example/lib/about.dart rename to tdesign-component/example/lib/about.dart index c993392c9..50b4f6fb8 100644 --- a/example/lib/about.dart +++ b/tdesign-component/example/lib/about.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:tdesign_flutter/td_export.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; class AboutPage extends StatefulWidget { const AboutPage({Key? key}) : super(key: key); @@ -13,10 +13,13 @@ class _AboutPageState extends State { String? version; + String? publishTime; + @override void initState() { super.initState(); _getVersion(); + _getPublishTime(); } Future _getVersion() async { @@ -24,7 +27,12 @@ class _AboutPageState extends State { setState(() {}); } - + Future _getPublishTime() async { + var timeStamp = await rootBundle.loadString('assets/publish_time'); + var exactTime = DateTime.fromMillisecondsSinceEpoch(int.parse(timeStamp.trim())); + publishTime = '${exactTime.year}-${exactTime.month}-${exactTime.day}'; + setState(() {}); + } @override @@ -35,7 +43,8 @@ class _AboutPageState extends State { body: SingleChildScrollView( child: Column( children: [ - demoRow(context, '版本号:',desc: version) + demoRow(context, '版本号:',desc: version), + demoRow(context, '发版日期:', desc: publishTime) ], ), ), diff --git a/tdesign-component/example/lib/annotation/demo.dart b/tdesign-component/example/lib/annotation/demo.dart new file mode 100644 index 000000000..01fd7a970 --- /dev/null +++ b/tdesign-component/example/lib/annotation/demo.dart @@ -0,0 +1,11 @@ +/// 生成代码的注解 +class Demo { + + /// 注解分组,其与标注的方法名唯一表示一份演示代码文件 + /// 请直接写字符串,不要做拼接或变量引用等操作 + final String? group; + + const factory Demo({String? group}) = Demo._; + + const Demo._({this.group}); +} \ No newline at end of file diff --git a/tdesign-component/example/lib/base/api_widget.dart b/tdesign-component/example/lib/base/api_widget.dart new file mode 100644 index 000000000..e197a2d6b --- /dev/null +++ b/tdesign-component/example/lib/base/api_widget.dart @@ -0,0 +1,97 @@ + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:markdown/markdown.dart' as md; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import 'example_base.dart'; + +/// API展示页面 +class ApiPage extends StatelessWidget { + const ApiPage({Key? key, this.model}) : super(key: key); + + final ExamplePageModel? model; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: TDText('${model?.text} API', textColor: TDTheme.of(context).whiteColor1,),), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16), + child: ApiWidget(apiName: model?.name, visible: true,), + ), + ), + ); + } +} + + +class ApiWidget extends StatefulWidget { + const ApiWidget({Key? key, required this.apiName, this.visible = false}) : super(key: key); + + final bool visible; + + final String? apiName; + + @override + State createState() => _ApiWidgetState(); +} + +class _ApiWidgetState extends State { + + String? result; + String? lastApiName; + + @override + Widget build(BuildContext context) { + return Visibility( + visible: widget.visible, + child: FutureBuilder( + future: getApiData(), + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return Container( + margin: const EdgeInsets.only(bottom: 64), + child: Markdown( + padding: EdgeInsets.zero, + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + selectable: true, + data: snapshot.data ?? '', + extensionSet: md.ExtensionSet( + md.ExtensionSet.gitHubWeb.blockSyntaxes, + [md.EmojiSyntax(), ...md.ExtensionSet.gitHubWeb.inlineSyntaxes], + ), + ), + ); + } else { + return Container( + alignment: Alignment.topLeft, + child: const TDText('加载中…'), + ); + } + }, + )); + } + + Future getApiData() async { + const defaultResult = ''' +## API + +暂无对应api + '''; + if(widget.apiName == lastApiName && result != null && result != defaultResult){ + return result!; + } + try { + var apiName = widget.apiName ?? 'default'; + result = await rootBundle.loadString('assets/api/${apiName}_api.md'); + lastApiName = widget.apiName; + } catch (e) { + print('getApiData error: $e'); + } + return result ?? defaultResult; + } +} \ No newline at end of file diff --git a/tdesign-component/example/lib/base/example_base.dart b/tdesign-component/example/lib/base/example_base.dart new file mode 100644 index 000000000..8ee72b3e1 --- /dev/null +++ b/tdesign-component/example/lib/base/example_base.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import 'api_widget.dart'; + + +typedef PageBuilder = Widget Function(BuildContext context, ExamplePageModel model); + +/// 示例页面数据 +class ExamplePageModel{ + + ExamplePageModel({required this.text,required this.name, this.apiVisible = false, this.showAction = false, this.isTodo = false,this.pageName,required this.pageBuilder,}); + + final String text; + final String name; + String? codePath; + String? spline; + bool apiVisible; + bool showAction; + String? pageName; + bool isTodo; + final PageBuilder pageBuilder; +} + + + +/// 存储主题数据的内部控件 +class ExamplePageInheritedTheme extends InheritedWidget { + final ExamplePageModel model; + + const ExamplePageInheritedTheme( + {required this.model, Key? key, required Widget child}) + : super(key: key, child: child); + + @override + bool updateShouldNotify(covariant ExamplePageInheritedTheme oldWidget) { + return model != oldWidget.model; + } +} + +class ScreenUtil{ + + static bool isLargeScreen(BuildContext context){ + + var height = MediaQuery.of(context).size.height; + var width = MediaQuery.of(context).size.width; + return width > height; + } + + static bool isWebLargeScreen(BuildContext context){ + return PlatformUtil.isWeb && isLargeScreen(context); + } +} \ No newline at end of file diff --git a/tdesign-component/example/lib/base/example_route.dart b/tdesign-component/example/lib/base/example_route.dart new file mode 100644 index 000000000..6ae2acb6e --- /dev/null +++ b/tdesign-component/example/lib/base/example_route.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import '../about.dart'; +import '../config.dart'; +import '../home.dart'; +import 'api_widget.dart'; +import 'example_base.dart'; +import 'example_widget.dart'; + +class TDExampleRoute { + static final Map pageModelList = {}; + static const String aboutPath = 'about'; + static const String apiPath = 'api'; + + static String getApiPath(ExamplePageModel? model) { + return '$apiPath?${model?.name}'; + } + + static void init() { + exampleMap.forEach((key, value) { + value.forEach((model) { + pageModelList[model.name] = model; + }); + }); + // 添加关于页路由 + pageModelList[aboutPath] = ExamplePageModel( + text: '关于', + name: 'AboutPage', + pageBuilder: (context, model) => const AboutPage()); + } + + static void add(ExamplePageModel model) { + pageModelList[model.name] = model; + } + + static Route? onGenerateRoute(RouteSettings settings) { + final url = settings.name ?? 'unknown'; + var strings = url.split('?'); + var name = strings[0]; + var model = pageModelList[name]; + var paramsMap = {}; + if (strings.length > 1) { + var params = strings[1].split('&'); + params.forEach((element) { + var kv = element.split('='); + var key = kv[0]; + var value = ''; + if (kv.length > 1) { + value = kv[1]; + } + paramsMap[key] = value; + }); + } + if (model != null) { + if (paramsMap['showAction'] == '1') { + model.showAction = true; + } + final Route route = MaterialPageRoute( + settings: settings, + builder: (context) => model.pageBuilder(context, model)); + return route; + } else { + if (name.startsWith(apiPath)) { + if (strings.length > 1) { + var component = strings[1]; + final Route route = MaterialPageRoute( + settings: settings, + builder: (context) => ApiPage( + model: pageModelList[component], + )); + return route; + } + } + return MaterialPageRoute( + settings: settings, + builder: (context) => Center( + child: Text('error, url:${url}'), + )); + } + } +} diff --git a/tdesign-component/example/lib/base/example_widget.dart b/tdesign-component/example/lib/base/example_widget.dart new file mode 100644 index 000000000..d0a8deaed --- /dev/null +++ b/tdesign-component/example/lib/base/example_widget.dart @@ -0,0 +1,651 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:markdown/markdown.dart' as md; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../page/td_theme_page.dart'; +import 'syntax_highlighter.dart'; +import 'api_widget.dart'; +import 'example_base.dart'; +import 'example_route.dart'; +import 'notification_center.dart'; +import 'web_md_tool.dart'; + +var navBarkey = GlobalKey(); + +/// 示例页面控件,建议每个页面返回一个ExampleWidget即可,不用独自封装 +class ExamplePage extends StatefulWidget { + const ExamplePage({ + Key? key, + this.navBarKey, + required this.title, + this.desc = '', + this.children = const [], + this.padding, + this.backgroundColor, + required this.exampleCodeGroup, + this.test = const [], + this.showSingleChild = false, + this.singleChild, + this.scrollController, + this.floatingActionButton, + }) : assert(children.length > 0 || (showSingleChild && singleChild != null), + 'children or singleChild must have at least one'), + super(key: key); + + /// 标题 + final String title; + + /// 如果封装的children无法满足需求,可以自定义子控件 + final bool showSingleChild; + + /// 自定义的自控件,只有showSingleChild为true才会展示。CodeWrapper的builder构建真正的试图 + final CodeWrapper? singleChild; + + /// 示例组件模块列表 + final List children; + + /// 描述,showSingleChild为false会展示 + final String desc; + + /// 填充 + final EdgeInsetsGeometry? padding; + + /// 背景颜色 + final Color? backgroundColor; + + /// 示例代码路径 + final String exampleCodeGroup; + + /// 测试组件列表 + final List test; + + /// 滚动控制组件 + final ScrollController? scrollController; + + /// 悬浮按钮 + final Widget? floatingActionButton; + + /// 悬浮按钮 + final GlobalKey? navBarKey; + + @override + State createState() => _ExamplePageState(); +} + +class _ExamplePageState extends State { + late List list; + bool apiVisible = false; + ExamplePageModel? model; + bool showAction = false; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + var modelTheme = context + .dependOnInheritedWidgetOfExactType(); + model = modelTheme?.model; + model?.codePath = widget.exampleCodeGroup; + model?.apiVisible = apiVisible; + setState(() { + showAction = model?.showAction ?? false; + }); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + floatingActionButton: widget.floatingActionButton, + backgroundColor: + widget.backgroundColor ?? TDTheme.of(context).grayColor1, + body: ScrollbarTheme( + data: ScrollbarThemeData( + trackVisibility: MaterialStateProperty.all(true)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildNavBar(), + Expanded( + child: widget.showSingleChild && widget.singleChild != null + ? _singleChild() + : MediaQuery( + // 去掉底部安全区域,保证示例展示正常 + data: MediaQuery.of(context).copyWith(padding: EdgeInsets.zero), + child: ListView.builder( + controller: widget.scrollController, + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(top: 24, bottom: 24), + itemCount: widget.children.length + 3, + itemBuilder: (context, index) { + if (index == 0) { + return _buildHeader(context); + } + if (index == widget.children.length + 2) { + return WebMdTool.needGenerateWebMd + ? Container( + margin: const EdgeInsets.only(top: 24), + child: Column( + children: [ + TDButton( + text: '生成Web使用md', + type: TDButtonType.fill, + onTap: () => WebMdTool.generateWebMd( + model: model, + description: widget.desc, + exampleCodeGroup: + widget.exampleCodeGroup, + exampleModuleList: + widget.children, + testList: widget.test, + singleChild: + widget.showSingleChild + ? widget.singleChild + : null), + ), + TDButton( + text: '返回首页', + type: TDButtonType.fill, + onTap: () => Navigator.of(context).maybePop(), + ), + ], + ), + ) + : Container(); + } + ExampleModule data; + if (index <= widget.children.length) { + data = widget.children[index - 1]; + } else { + data = ExampleModule(title: '单元测试', children: [ + _buildTestExampleItem(), + ...widget.test + ]); + } + return _buildModule(index, data, context); + }, + ), + )), + ], + ))); + } + + Widget _singleChild() { + if (!WebMdTool.needGenerateWebMd) { + return widget.singleChild!; + } + return ExampleItemInherited( + child: Stack( + children: [ + widget.singleChild!, + Positioned( + left: 16, + right: 16, + bottom: 0, + child: Column( + children: [ + TDButton( + text: '生成Web使用md', + type: TDButtonType.fill, + onTap: () => WebMdTool.generateWebMd( + model: model, + description: widget.desc, + exampleCodeGroup: widget.exampleCodeGroup, + exampleModuleList: widget.children, + testList: widget.test, + singleChild: + widget.showSingleChild ? widget.singleChild : null), + ), + TDButton( + text: '返回首页', + type: TDButtonType.fill, + onTap: () => Navigator.of(context).maybePop(), + ), + ], + )), + ], + ), + path: widget.exampleCodeGroup, + ); + } + + ExampleItem _buildTestExampleItem() => + ExampleItem(desc: '''未在示例稿中体现,但有必要验证的组件样式,请添加到'test'参数中。以下情景必须有测试: + 1.参数为数字。需测试数字为负数、0、较大数值的场景。 + 2.参数为枚举,需测试所有枚举组合(示例已有的可不写)''', builder: (_) => const TDDivider()); + + Widget _buildNavBar() { + var rightBarItems = []; + + // web端示例页不展示标题栏 + if (PlatformUtil.isWeb && !Navigator.canPop(context)) { + return Container(); + } + if (showAction && !PlatformUtil.isWeb) { + rightBarItems.add(TDNavBarItem( + icon: TDIcons.info_circle, + action: () { + Navigator.pushNamed(context, TDExampleRoute.getApiPath(model)); + })); + if (!PlatformUtil.isWeb) { + rightBarItems.add(TDNavBarItem( + icon: TDIcons.code, + action: () { + setState(() { + apiVisible = !apiVisible; + if (model != null) { + model!.apiVisible = apiVisible; + } + }); + TNotification.postNotification( + 'onApiVisibleChange', {'apiVisible': apiVisible}); + })); + } + } + return TDNavBar( + key: widget.navBarKey, + title: widget.title, + rightBarItems: rightBarItems, + ); + } + + Widget _buildHeader(BuildContext context) { + if (widget.showSingleChild) { + return Container(); + } + return Container( + margin: const EdgeInsets.only( + left: 16, + right: 16, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if(WebMdTool.needGenerateWebMd) const TDText('WebGenTag'), + TDText( + widget.title, + font: TDTheme.of(context).fontHeadlineSmall, + textColor: TDTheme.of(context).fontGyColor1, + ), + Container( + margin: const EdgeInsets.only( + top: 4, + ), + child: TDText( + widget.desc, + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2, + ), + ), + // Expanded(child: ), + ], + ), + ); + } + + Widget _buildModule(int index, ExampleModule data, BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: const EdgeInsets.only(left: 16, right: 16, top: 32), + child: TDText( + '${index < 10 ? "0$index" : index} ${data.title}', + font: TDTheme.of(context).fontTitleLarge, + textColor: TDTheme.of(context).fontGyColor1, + fontWeight: FontWeight.bold, + ), + ), + for (var index = 0; index < data.children.length; index++) + _buildExampleItem(data, index) + ], + ); + } + + Widget _buildExampleItem(ExampleModule data, int index) { + return Container( + margin: widget.padding, + child: ExampleItemWidget( + data: data.children[index], + index: index, + exampleCodeGroup: widget.exampleCodeGroup, + moduleTitle: data.title, + ), + ); + } +} + +/// 示例模块 +class ExampleModule { + const ExampleModule({Key? key, required this.title, required this.children}); + + final String title; + + final List children; +} + +/// 示例样例数据 +class ExampleItem { + const ExampleItem( + {Key? key, + this.desc = '', + required this.builder, + this.methodName, + this.center = true, + this.ignoreCode = false, + this.padding}); + + final String desc; + + final WidgetBuilder builder; + + final String? methodName; + + final bool center; + + final bool ignoreCode; + + final EdgeInsetsGeometry? padding; +} + +/// 组件示例 +class ExampleItemInherited extends InheritedWidget { + const ExampleItemInherited( + {required this.path, Key? key, required Widget child}) + : super(key: key, child: child); + + final String path; + + @override + bool updateShouldNotify(covariant ExampleItemInherited oldWidget) { + return path != oldWidget.path; + } +} + +/// 组件示例 +class ExampleItemWidget extends StatefulWidget { + const ExampleItemWidget( + {required this.data, + Key? key, + required this.index, + this.exampleCodeGroup, + this.moduleTitle}) + : super(key: key); + + final ExampleItem data; + final int index; + final String? exampleCodeGroup; + final String? moduleTitle; + + @override + State createState() => _ExampleItemWidgetState(); +} + +class _ExampleItemWidgetState extends State { + @override + Widget build(BuildContext context) { + Widget child; + if (widget.data.ignoreCode) { + child = widget.data.builder(context); + if (widget.data.center) { + child = Center( + child: widget.data.builder(context), + ); + } + } else { + child = CodeWrapper( + builder: widget.data.builder, + methodName: widget.data.methodName, + isCenter: widget.data.center, + isFromItem: true, + ); + } + if (widget.data.padding != null) { + child = Padding( + padding: widget.data.padding!, + child: child, + ); + } + child = Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: widget.data.center + ? CrossAxisAlignment.center + : CrossAxisAlignment.start, + children: [ + widget.data.desc.isEmpty + ? Container() + : Container( + alignment: Alignment.topLeft, + margin: EdgeInsets.only( + left: 16, + right: 16, + top: widget.index == 0 ? 8 : 24, + bottom: 16), + child: TDText( + widget.data.desc, + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2, + ), + ), + child + ], + ); + return ExampleItemInherited( + child: child, + path: WebMdTool.getItemKey( + widget.exampleCodeGroup, widget.moduleTitle, widget.data.desc), + ); + } +} + +class CodeWrapper extends StatefulWidget { + const CodeWrapper( + {Key? key, + required this.builder, + this.methodName, + this.isCenter = false, + this.isFromItem = false}) + : super(key: key); + + final WidgetBuilder builder; + + final bool isCenter; + + final bool isFromItem; + + final String? methodName; + + @override + State createState() => _CodeWrapperState(); +} + +class _CodeWrapperState extends State { + bool apiVisible = false; + + String exampleCodeGroup = ''; + + String? codeString; + + @override + void initState() { + super.initState(); + TNotification.addObserver('onApiVisibleChange', (arguments) { + if (arguments is Map) { + setState(() { + apiVisible = arguments['apiVisible'] ?? false; + }); + } + }); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + setState(() { + var modelTheme = context + .dependOnInheritedWidgetOfExactType(); + exampleCodeGroup = modelTheme?.model.codePath ?? ''; + apiVisible = modelTheme?.model.apiVisible ?? false; + }); + + if (WebMdTool.needGenerateWebMd && !widget.isFromItem) { + loadManualCode(); + } + }); + } + + void loadManualCode() async { + var modelTheme = + context.dependOnInheritedWidgetOfExactType(); + if (modelTheme?.path != null) { + codeString ??= await loadCodeString(); + var list = WebMdTool.manualExampleCode[modelTheme!.path] ?? []; + list.add(codeString!); + WebMdTool.manualExampleCode[modelTheme.path] = list; + } + } + + @override + Widget build(BuildContext context) { + var child = widget.builder(context); + if (widget.isCenter) { + child = Center( + child: widget.builder(context), + ); + } + if (apiVisible) { + child = Stack( + children: [ + child, + Positioned( + top: 0, + bottom: 0, + left: 0, + right: 0, + child: GestureDetector( + onTap: _showCodePanel, + child: Container( + color: Colors.black.withOpacity(0.4), + alignment: Alignment.center, + child: TDText( + 'code', + textColor: TDTheme.of(context).whiteColor1, + ), + ), + )) + ], + ); + } + return child; + } + + String _getCodeAssetsPath() { + var methodName = widget.methodName ?? ''; + + var builderString = widget.builder.toString(); + if (methodName.isEmpty) { + if (builderString.contains('\'')) { + var strings = builderString.split('\''); + if (strings.length > 1) { + methodName = strings[1]; + if (methodName.isNotEmpty && methodName.contains('@')) { + methodName = methodName.split('@')[0]; + } + } + } + } + if (methodName.isNotEmpty && exampleCodeGroup.isNotEmpty) { + print('example code methodName: $methodName'); + return 'assets/code/${exampleCodeGroup}.$methodName.txt'; + } + return ''; + } + + void _showCodePanel() async { + codeString ??= await loadCodeString(); + await showModalBottomSheet( + isScrollControlled: true, + barrierColor: Colors.black.withOpacity(0.5), + backgroundColor: Colors.transparent, + context: context, + builder: (_) { + if (codeString!.isEmpty) { + return Container( + alignment: Alignment.center, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.vertical( + top: Radius.circular(TDTheme.of(context).radiusDefault))), + height: 500, + child: + TDText(PlatformUtil.isWeb ? 'web不支持演示代码,请在移动端查看' : '暂无演示代码'), + ); + } + + var lines = codeString!.split('\n'); + print('lines: ${lines.length}'); + double height = min(max(300, lines.length * 17 + 32), + MediaQuery.of(context).size.height - 150); + var mdText = ''' +```dart +${codeString} +``` + '''; + return Container( + alignment: Alignment.center, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.vertical( + top: Radius.circular(TDTheme.of(context).radiusDefault))), + height: height, + child: Markdown( + physics: const BouncingScrollPhysics(), + padding: EdgeInsets.zero, + selectable: false, + shrinkWrap: true, + syntaxHighlighter: DartSyntaxHighlighter(), + data: mdText, + extensionSet: md.ExtensionSet( + md.ExtensionSet.gitHubWeb.blockSyntaxes, + [md.EmojiSyntax(), ...md.ExtensionSet.gitHubWeb.inlineSyntaxes], + ), + ), + ); + }); + } + + Future loadCodeString() async { + var codeString; + var assetsPath = _getCodeAssetsPath(); + if (assetsPath.isNotEmpty) { + try { + codeString = await rootBundle.loadString(assetsPath); + } catch (e) { + print(e); + } + } + return codeString; + } +} + +/// State获取标题的扩展 +extension TDStateExs on State { + String tdTitle() { + var modelTheme = + context.dependOnInheritedWidgetOfExactType(); + return modelTheme?.model.text ?? ''; + } +} + +/// StatelessWidget获取标题的扩展 +extension TDWidgetExs on StatelessWidget { + String tdTitle(BuildContext context) { + var modelTheme = + context.dependOnInheritedWidgetOfExactType(); + return modelTheme?.model.text ?? ''; + } +} diff --git a/tdesign-component/example/lib/base/intl_resource_delegate.dart b/tdesign-component/example/lib/base/intl_resource_delegate.dart new file mode 100644 index 000000000..33c5ac400 --- /dev/null +++ b/tdesign-component/example/lib/base/intl_resource_delegate.dart @@ -0,0 +1,165 @@ +import 'package:flutter/cupertino.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../localizations/app_localizations.dart'; + +/// 国际化资源代理 +class IntlResourceDelegate extends TDResourceDelegate { + IntlResourceDelegate(this.context); + + BuildContext context; + + /// 国际化需要每次更新context + updateContext(BuildContext context) { + this.context = context; + } + + @override + String get badgeZero => '0'; + + @override + String get cancel => AppLocalizations.of(context)!.cancel; + + @override + String get confirm => AppLocalizations.of(context)!.confirm; + + @override + String get close => AppLocalizations.of(context)!.switchClose; + + @override + String get open => AppLocalizations.of(context)!.switchOpen; + + @override + String get knew => AppLocalizations.of(context)!.knew; + + @override + String get loading => AppLocalizations.of(context)!.loading; + + @override + String get loadingWithPoint => AppLocalizations.of(context)!.loadingWithPoint; + + @override + String get other => AppLocalizations.of(context)!.other; + + @override + String get refreshing => AppLocalizations.of(context)!.refreshing; + + @override + String get releaseRefresh => AppLocalizations.of(context)!.releaseRefresh; + + @override + String get pullToRefresh => AppLocalizations.of(context)!.pullToRefresh; + + @override + String get completeRefresh => AppLocalizations.of(context)!.completeRefresh; + + @override + String get reset => AppLocalizations.of(context)!.reset; + + @override + String get days => AppLocalizations.of(context)!.days; + + @override + String get hours => AppLocalizations.of(context)!.hours; + + @override + String get milliseconds => AppLocalizations.of(context)!.milliseconds; + + @override + String get minutes => AppLocalizations.of(context)!.minutes; + + @override + String get seconds => AppLocalizations.of(context)!.seconds; + + @override + String get yearLabel => AppLocalizations.of(context)!.yearLabel; + + @override + String get monthLabel => AppLocalizations.of(context)!.monthLabel; + + @override + String get dateLabel => AppLocalizations.of(context)!.dateLabel; + + @override + String get weeksLabel => AppLocalizations.of(context)!.dateLabel; + + String get friday => AppLocalizations.of(context)!.friday; + + @override + String get monday => AppLocalizations.of(context)!.monday; + + @override + String get saturday => AppLocalizations.of(context)!.saturday; + + @override + String get sunday => AppLocalizations.of(context)!.sunday; + + @override + String get thursday => AppLocalizations.of(context)!.thursday; + + @override + String get tuesday => AppLocalizations.of(context)!.tuesday; + + @override + String get wednesday => AppLocalizations.of(context)!.wednesday; + + @override + String get year => AppLocalizations.of(context)!.year; + + @override + String get january => AppLocalizations.of(context)!.january; + + @override + String get february => AppLocalizations.of(context)!.february; + + @override + String get march => AppLocalizations.of(context)!.march; + + @override + String get april => AppLocalizations.of(context)!.april; + + @override + String get may => AppLocalizations.of(context)!.may; + + @override + String get june => AppLocalizations.of(context)!.june; + + @override + String get july => AppLocalizations.of(context)!.july; + + @override + String get august => AppLocalizations.of(context)!.august; + + @override + String get september => AppLocalizations.of(context)!.september; + + @override + String get october => AppLocalizations.of(context)!.october; + + @override + String get november => AppLocalizations.of(context)!.november; + + @override + String get december => AppLocalizations.of(context)!.december; + + @override + String get time => AppLocalizations.of(context)!.time; + + @override + String get start => AppLocalizations.of(context)!.start; + + @override + String get end => AppLocalizations.of(context)!.end; + + @override + String get notRated => AppLocalizations.of(context)!.notRated; + + @override + String get cascadeLabel => AppLocalizations.of(context)!.cascadeLabel; + + @override + String get back => AppLocalizations.of(context)!.back; + + @override + String get top => AppLocalizations.of(context)!.top; +} diff --git a/tdesign-component/example/lib/base/notification_center.dart b/tdesign-component/example/lib/base/notification_center.dart new file mode 100644 index 000000000..a9d0be006 --- /dev/null +++ b/tdesign-component/example/lib/base/notification_center.dart @@ -0,0 +1,55 @@ +import 'dart:collection'; + +typedef Observer = void Function(dynamic arguments); + +/// 广播工具 +class TNotification { + static final Map> _eventMap = HashMap(); + + static String addObserver(String eventName, Observer observer) { + if (eventName.isNotEmpty) { + var observerMap = _eventMap[eventName]; + observerMap ??= HashMap(); + var observerId = '${eventName}_${observer.hashCode}'; + observerMap[observerId] = observer; + _eventMap[eventName] = observerMap; + return observerId; + } + return ''; + } + + static void removeObserver(String eventName, String? observerId) { + if (observerId == null) { + return; + } + if (eventName.isNotEmpty) { + var listenerMap = _eventMap[eventName]; + listenerMap?.remove(observerId); + if ((listenerMap?.length ?? 0) <= 0) { + _eventMap.remove(eventName); + } + } + } + + static void postNotification(String eventName, dynamic arguments) { + if (eventName.isNotEmpty) { + var handlerArguments = { + 'eventName':eventName, + 'argumentsObj':arguments + }; + _postNotificationCallHandler(handlerArguments); + } + } + + static void _postNotificationCallHandler(arguments) { + + var observerMap = _eventMap[arguments['eventName']]; + observerMap?.forEach((key, observer) { + try { + observer(arguments['argumentsObj']); + } catch (e) { + print('TNotification postNotificationCallHandler $key error: $e'); + } + }); + } +} diff --git a/example/lib/web/syntax_highlighter.dart b/tdesign-component/example/lib/base/syntax_highlighter.dart similarity index 100% rename from example/lib/web/syntax_highlighter.dart rename to tdesign-component/example/lib/base/syntax_highlighter.dart diff --git a/tdesign-component/example/lib/base/web_md_tool.dart b/tdesign-component/example/lib/base/web_md_tool.dart new file mode 100644 index 000000000..74c5b4a19 --- /dev/null +++ b/tdesign-component/example/lib/base/web_md_tool.dart @@ -0,0 +1,274 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import 'example_base.dart'; +import 'example_widget.dart'; + +class WebMdTool { + WebMdTool._(); + + /// 用于生成web端的md,正常使用不要开启 + static bool needGenerateWebMd = false; + + /// 生成web端的md + static void generateWebMd({ + required ExamplePageModel? model, + required String description, + required String? exampleCodeGroup, + required List exampleModuleList, + required List testList, + required CodeWrapper? singleChild, + }) async { + if (needGenerateWebMd && model != null && !kIsWeb) { + var pageName = 'td_${model.pageName ?? model.name}_page'; + var exampleCodeSb = StringBuffer(); + var count = 1; + if (singleChild != null) { + await writeSingleCode(exampleCodeSb, exampleCodeGroup,pageName); + } else { + for (var module in exampleModuleList) { + exampleCodeSb.writeln('### $count ${module.title}'); + for (var item in module.children) { + await writeCode(exampleCodeSb, item, exampleCodeGroup, module); + } + } + } + + var api = ''' +## API + +暂无对应api +'''; + try { + api = await rootBundle.loadString('assets/api/${model.name}_api.md'); + } catch (e) { + print(e); + } + var mdContent = _getTemplate(model.text, description, + model.spline ?? 'other', exampleCodeSb.toString(), api.toString(), pageName); + print('生成演示代码成功:\n${mdContent.substring(0,50)}...'); + + var path = ""; + if(Platform.environment['FLUTTER_TEST'] == 'true'){ + + var baseDir = Platform.script.toFilePath().split('/tdesign-component')[0]; + path = '$baseDir/tdesign-site/src/${model.name}/README.md'; + // path = '$baseDir/test/src/${model.name}/README.md'; + // File + } else { + path = '/sdcard/td/web_md/${model.name}/README.md'; + } + var file = File(path); + if (!file.existsSync()) { + file.createSync(recursive: true); + } + file.writeAsStringSync(mdContent); + } + } + + static Future writeSingleCode( + StringBuffer exampleCodeSb, String? exampleCodeGroup, String? pageName) async { + var hasCodeSuccess = false; + + var list = manualExampleCode[exampleCodeGroup]; + if (list != null && list.isNotEmpty) { + list.forEach((element) { + exampleCodeSb.writeln(''); + exampleCodeSb.writeln(''' + + + +
${element}
+ +
+ '''); + }); + hasCodeSuccess = true; + } else { + print('error item:singleChild_${exampleCodeGroup},已忽略代码,请手动处理'); + } + + if (!hasCodeSuccess) { + exampleCodeSb.writeln(''' + + + +
暂无演示代码
+ +
+ '''); + } + } + + static Future writeCode(StringBuffer exampleCodeSb, ExampleItem item, + String? exampleCodeGroup, ExampleModule module) async { + exampleCodeSb.writeln(''); + exampleCodeSb.writeln('${item.desc}'); + + var hasCodeSuccess = false; + if (!item.ignoreCode) { + var assetsPath = _getCodeAssetsPath(item, exampleCodeGroup ?? ''); + if (assetsPath.isNotEmpty) { + try { + var codeString = await rootBundle.loadString(assetsPath); + if (codeString.isNotEmpty) { + exampleCodeSb.writeln(''' + + + +
${codeString}
+ +
+ '''); + hasCodeSuccess = true; + } + } catch (e) { + print(e); + } + } + } else { + var list = manualExampleCode[ + getItemKey(exampleCodeGroup, module.title, item.desc)]; + if (list != null && list.isNotEmpty) { + list.forEach((element) { + exampleCodeSb.writeln(''); + exampleCodeSb.writeln(''' + + + +
${element}
+ +
+ '''); + }); + hasCodeSuccess = true; + } else { + print( + 'error item:${exampleCodeGroup}_${module.title}_${item.desc},已忽略代码,请手动处理'); + } + } + if (!hasCodeSuccess) { + exampleCodeSb.writeln(''' + + + +
暂无演示代码
+ +
+ '''); + } + } + + static String _getCodeAssetsPath(ExampleItem exampleItem, String group) { + var methodName = exampleItem.methodName ?? ''; + + var builderString = exampleItem.builder.toString(); + if (methodName.isEmpty) { + if (builderString.contains('\'')) { + var strings = builderString.split('\''); + if (strings.length > 1) { + methodName = strings[1]; + if (methodName.isNotEmpty && methodName.contains('@')) { + methodName = methodName.split('@')[0]; + } + } + } + } + if (methodName.isNotEmpty && group.isNotEmpty) { + print('example code methodName: $methodName'); + return 'assets/code/${group}.$methodName.txt'; + } + return ''; + } + + static String getSpline(String key) { + switch (key) { + case '基础': + return 'base'; + case '导航': + return 'base'; + case '输入': + return 'base'; + case '数据展示': + return 'base'; + case '反馈': + return 'base'; + default: + return 'other'; + } + } + + static String getItemKey(exampleCodeGroup, moduleTitle, itemDesc) { + return '${exampleCodeGroup}_${moduleTitle}_${itemDesc}'; + } + + static String _getTemplate( + String title, + String description, + String spline, + String exampleCode, + String api, + String pageName, + ) => + ''' +--- +title: $title +description: ${description} +spline: ${spline} +isComponent: true +--- + + +## 引入 + +在tdesign_flutter/tdesign_flutter.dart中有所有组件的路径。 + +```dart +import 'package:tdesign_flutter/tdesign_flutter.dart';${_getExtraImport(title)} +``` + +## 代码演示 + +${_getPageCode(pageName)} + +${exampleCode} + +${api} + + '''; + + static var manualExampleCode = >{}; + + static _getExtraImport(String title) { + if(title == 'Swiper 轮播图'){ + return ''' + +import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart';'''; + } else if(title == 'PullDownRefresh 下拉刷新'){ + return ''' + +import 'package:easy_refresh/easy_refresh.dart';'''; + + } + return ''; + } + + static _getPageCode(String pageName) { + if(pageName == 'td_side-bar_page'){ + return ''' +[td_sidebar_page.dart](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/example/lib/page/sidebar/td_sidebar_page.dart) + +[td_sidebar_page_anchor.dart](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/example/lib/page/sidebar/td_sidebar_page_anchor.dart) + +[td_sidebar_page_custom.dart](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/example/lib/page/sidebar/td_sidebar_page_custom.dart) + +[td_sidebar_page_icon.dart](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/example/lib/page/sidebar/td_sidebar_page_icon.dart) + +[td_sidebar_page_pagination.dart](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/example/lib/page/sidebar/td_sidebar_page_pagination.dart)'''; + } + return '[$pageName.dart](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/example/lib/page/$pageName.dart)'; + } +} diff --git a/tdesign-component/example/lib/component_test/confirm_dialog_test.dart b/tdesign-component/example/lib/component_test/confirm_dialog_test.dart new file mode 100644 index 000000000..0b8a58f1a --- /dev/null +++ b/tdesign-component/example/lib/component_test/confirm_dialog_test.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +void main() => runApp(const ConfirmDialogTestApp()); + +class ConfirmDialogTestApp extends StatelessWidget { + const ConfirmDialogTestApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'confirmDialog 测试示例', + theme: ThemeData(primarySwatch: Colors.blue), + home: const TestPage(), + ); + } +} + +class TestPage extends StatefulWidget { + const TestPage({super.key}); + + @override + State createState() => _TestPageState(); +} + +class _TestPageState extends State { + final TextEditingController _searchNameController = TextEditingController(); + final TextEditingController _searchRemarkController = TextEditingController(); + + void _showProblemDialog() { + showDialog( + context: context, + builder: (context) => TDConfirmDialog( + title: '搜索设备', + contentWidget: Column( + children: [ + TDInput( + leftIcon: const Icon(TDIcons.device), + controller: _searchNameController, + hintText: '设备名称', + ), + TDInput( + leftIcon: const Icon(TDIcons.pen_quill), + controller: _searchRemarkController, + hintText: '设备备注', + ), + ], + ), + buttonText: '确认', + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('TDConfirmDialog测试')), + body: Center( + child: TDButton( + child: const Text('显示问题弹窗'), + onTap: _showProblemDialog, + ), + ), + ); + } + + @override + void dispose() { + _searchNameController.dispose(); + _searchRemarkController.dispose(); + super.dispose(); + } +} \ No newline at end of file diff --git a/tdesign-component/example/lib/component_test/image_test.dart b/tdesign-component/example/lib/component_test/image_test.dart new file mode 100644 index 000000000..50b5f73f6 --- /dev/null +++ b/tdesign-component/example/lib/component_test/image_test.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +void main() async { + runApp(ImageTestApp()); +} + +class ImageTestApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Image Test', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: TestPage(), + ); + } +} + +class TestPage extends StatelessWidget { + final GlobalKey _formKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + + return Scaffold( + appBar: AppBar( + title: TDText('TDImage Test Page'), + ), + body: Form( + key: _formKey, + child: Column( + children: [ + + Image.network( + 'assets/img/image.png', + width: 335, + fit: BoxFit.fitWidth, + ), + SizedBox(height: 20), + + TDImage( + imgUrl: 'assets/img/image.png', + type: TDImageType.fitHeight, + height: 144, + fit: BoxFit.fitHeight, + ), + ], + ) + ), + ); + } +} diff --git a/tdesign-component/example/lib/component_test/popup_test.dart b/tdesign-component/example/lib/component_test/popup_test.dart new file mode 100644 index 000000000..901f4c1ef --- /dev/null +++ b/tdesign-component/example/lib/component_test/popup_test.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +void main() => runApp(const ConfirmDialogTestApp()); + +class ConfirmDialogTestApp extends StatelessWidget { + const ConfirmDialogTestApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'confirmDialog 测试示例', + theme: ThemeData(primarySwatch: Colors.blue), + home: const TestPage(), + ); + } +} + +class TestPage extends StatefulWidget { + const TestPage({super.key}); + + @override + State createState() => _TestPageState(); +} + +class _TestPageState extends State { + final TextEditingController _searchNameController = TextEditingController(); + final TextEditingController _searchRemarkController = TextEditingController(); + + void _showProblemDialog() { + + Navigator.of(context).push( + TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: "title", + radius: 20, + backgroundColor: Color(0xFFFAFFFC), + closeClick: () { + Navigator.maybePop(context); + }, + child: Container( + padding: EdgeInsets.only(left: 20, right: 20, bottom: 33), + decoration: BoxDecoration(color: Colors.white), + child: Column( + children: [ + Center( + child: Text("立即拨打"), + ), + ], + ), + ), + ); + }, + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: const Text('TDConfirmDialog测试')), + body: Center( + child: TDButton( + child: const Text('显示问题弹窗'), + onTap: _showProblemDialog, + ), + ), + ); + } + + @override + void dispose() { + _searchNameController.dispose(); + _searchRemarkController.dispose(); + super.dispose(); + } +} \ No newline at end of file diff --git a/tdesign-component/example/lib/component_test/step_test.dart b/tdesign-component/example/lib/component_test/step_test.dart new file mode 100644 index 000000000..b37246cf4 --- /dev/null +++ b/tdesign-component/example/lib/component_test/step_test.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +void main() async { + runApp(StepTestApp()); +} + +class StepTestApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Step Test', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: TestPage(), + ); + } +} + +class TestPage extends StatelessWidget { + final GlobalKey _formKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + // 创建水平步骤条的数据 + List horizontalSteps = [ + TDStepsItemData(title: 'Step 1', content: 'Horizontal Step 1'), + TDStepsItemData(title: 'Step 2', content: 'Horizontal Step 2'), + TDStepsItemData(title: 'Step 3', content: 'Horizontal Step 3'), + ]; + + // 创建垂直步骤条的数据 + List verticalSteps = [ + TDStepsItemData( + title: '2025-01-11', + content: '今天是星期六', + customContent: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TDText( + '今天是星期六,下面是拍摄的照片', + style: TextStyle( + fontWeight: FontWeight.w400, + color: TDTheme.of(context).fontGyColor3, + fontSize: 12, + ), + ), + Image.asset('assets/img/image.png', width: 100, height: 100), + ], + ), + ), + TDStepsItemData(title: '2025-01-12', content: '今天是星期天'), + TDStepsItemData(content: '今天是星期一'), + ]; + + return Scaffold( + appBar: AppBar( + title: TDText('TDSteps Test Page'), + ), + body: Form( + key: _formKey, + child: Column( + children: [ + // 水平步骤条 + Expanded( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: TDSteps( + steps: horizontalSteps, + activeIndex: 1, // 设置当前激活的步骤索引 + direction: TDStepsDirection.horizontal, // 设置步骤条方向为水平 + status: TDStepsStatus.success, // 设置步骤条状态 + ), + ), + ), + // 垂直步骤条 + Expanded( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: TDSteps( + steps: verticalSteps, + activeIndex: 1, // 设置当前激活的步骤索引 + direction: TDStepsDirection.vertical, // 设置步骤条方向为垂直 + status: TDStepsStatus.success, // 设置步骤条状态 + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/tdesign-component/example/lib/component_test/tabbar_test.dart b/tdesign-component/example/lib/component_test/tabbar_test.dart new file mode 100644 index 000000000..1bd118624 --- /dev/null +++ b/tdesign-component/example/lib/component_test/tabbar_test.dart @@ -0,0 +1,178 @@ +import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import 'package:tdesign_flutter/src/util/string_util.dart'; +// import 'package:xjy_study/utils/color_util.dart'; + +class StudyDetail extends StatefulWidget { + const StudyDetail({super.key}); + + @override + State createState() => _StudyDetailState(); +} + +class _StudyDetailState extends State with SingleTickerProviderStateMixin { + final List _tabs = const ['待上课时', '已上课时']; + + late TabController _tabController; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: _tabs.length, vsync: this); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _getQueryParams(); + } + + @override + void dispose() { + super.dispose(); + } + + void _getQueryParams() { + // final ModalRoute? currentRoute = ModalRoute.of(context); + // if (currentRoute != null) { + // final Map params = currentRoute.settings.arguments as Map; + // print('aa=${params}'); + // } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('课程详情'), + leadingWidth: 40.w, + ), + body: ExtendedNestedScrollView( + onlyOneScrollInBody: true, + physics: ClampingScrollPhysics(), + headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { + return [ + SliverToBoxAdapter( + child: _CourseItemDetail(), + ) + ]; + }, + body: Column( + children: [ + TDTabBar( + controller: _tabController, + height: 44.h, + backgroundColor: Colors.white, + indicatorColor: TDTheme.of().brandNormalColor, + // labelColor:TDTheme.of().brandNormalColor, + unselectedLabelStyle: TextStyle(fontSize: 12.sp, color: Colors.red), + labelStyle: TextStyle( + fontSize: 28.sp, + color: Colors.deepPurpleAccent, + fontWeight: FontWeight.w500), + indicatorWidth: 16.w, + showIndicator: true, + tabs: _tabs + .map((e) => TDTab( + text: '$e', + )) + .toList()), + Expanded( + child: TDTabBarView( + isSlideSwitch: true, + controller: _tabController, + children: _tabs + .map((e) => Center( + child: Text('data$e'), + )) + .toList())) + ], + ), + ), + ); + } +} + +/// 课程详情描述 +class _CourseItemDetail extends StatelessWidget { + const _CourseItemDetail(); + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.fromLTRB(15.w, 10.h, 15.w, 8.h), + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.only(top: 3.h), + height: 16.h, + alignment: Alignment.center, + padding: EdgeInsets.symmetric(horizontal: 6.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(4)), + gradient: LinearGradient(colors: [ + Color(0xFFFFB442), + Color(0xFFFF9A00), + ])), + child: Text( + '数学', + style: TextStyle(color: Colors.white, fontSize: 12.sp, height: 1.h), + ), + ), + SizedBox( + width: 8.w, + ), + Expanded( + child: Text( + '集合图形离开撒娇的案例三等奖集合图形离开撒娇的案例', + style: TextStyle( + color: TDTheme.of().fontGyColor1, + fontSize: 14.sp, + overflow: TextOverflow.ellipsis, + height: 1.5.h), + maxLines: 2, + )) + ], + ), + Padding( + padding: EdgeInsets.only(top: 5.h, bottom: 12.h), + child: Text( + '2020年8月15日开始,共20节课', + style: TextStyle(fontSize: 12.sp, color: TDTheme.of().fontGyColor2,), + ), + ), + Row( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(28.w), + child: Container( + width: 28.w, + height: 28.w, + color: Colors.grey, + ), + ), + SizedBox( + width: 10.w, + ), + Text( + '某某老师', + maxLines: 1, + style: TextStyle( + fontSize: 12.sp, + color: TDTheme.of().fontGyColor2, + overflow: TextOverflow.ellipsis), + ) + ], + ) + ], + ), + ); + } +} \ No newline at end of file diff --git a/tdesign-component/example/lib/component_test/tag_test.dart b/tdesign-component/example/lib/component_test/tag_test.dart new file mode 100644 index 000000000..45e11c1bf --- /dev/null +++ b/tdesign-component/example/lib/component_test/tag_test.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +void main() async { + runApp(TagTestApp()); +} + +class TagTestApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'TDTag 宽度测试', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: TestPage(), + ); + } +} + +class TestPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: TDText('TDTag 宽度测试'), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSection(context), + _buildFixedWidthSection(context), + _buildEdgeCaseSection(context), + ], + ), + ), + ); + } + + Widget _buildSection(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TDText('不带宽度测试', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + const SizedBox(height: 12), + Wrap( + spacing: 12, + runSpacing: 12, + children: const [ + TDTag('1', + theme: TDTagTheme.primary, + size: TDTagSize.medium, + ), + TDTag('1000', + theme: TDTagTheme.warning, + ), + TDTag('文本', + theme: TDTagTheme.success, + ), + ], + ), + const SizedBox(height: 24), + ], + ); + } + + Widget _buildFixedWidthSection(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TDText('基础固定宽度测试', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + const SizedBox(height: 12), + Wrap( + spacing: 12, + runSpacing: 12, + children: const [ + TDTag('1', + fixedWidth: 80, + theme: TDTagTheme.primary, + size: TDTagSize.medium, + ), + TDTag('1000', + fixedWidth: 80, + theme: TDTagTheme.warning, + ), + TDTag('文本', + fixedWidth: 80, + theme: TDTagTheme.success, + ), + ], + ), + const SizedBox(height: 24), + ], + ); + } + + Widget _buildEdgeCaseSection(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TDText('边界情况测试', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + const SizedBox(height: 12), + const TDTag('超长文本测试超长文本测试超长文本测试超长文本测试', + fixedWidth: 100, + theme: TDTagTheme.warning, + ), + const SizedBox(height: 12), + const TDTag('带关闭按钮', + fixedWidth: 150, + needCloseIcon: true, + theme: TDTagTheme.danger, + ), + const SizedBox(height: 12), + TDTag('动态宽度', + fixedWidth: MediaQuery.of(context).size.width * 0.5, + theme: TDTagTheme.success, + ), + const SizedBox(height: 12), + const TDTag('极小宽度', + fixedWidth: 50, + theme: TDTagTheme.primary, + ), + ], + ); + } +} \ No newline at end of file diff --git a/tdesign-component/example/lib/component_test/test_app.dart b/tdesign-component/example/lib/component_test/test_app.dart new file mode 100644 index 000000000..21c062d92 --- /dev/null +++ b/tdesign-component/example/lib/component_test/test_app.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import 'tabbar_test.dart'; + +void main() async { + kTextNeedGlobalFontFamily = true; + WidgetsFlutterBinding.ensureInitialized(); + var jsonString = await rootBundle.loadString('assets/theme.json'); + print('jsonString:$jsonString'); + TDTheme.needMultiTheme(true); + TDTheme.defaultData(); + var themeData = TDThemeData.fromJson('green', jsonString); + await TDFontLoader.load( + name: 'test1', + fontFamilyUrl: + 'https://xinyue.qq.com/m/flutter_web/assets/packages/flutter_component/fonts/FZLanTingHeiS-EB-GB.ttf'); + runApp(MaterialApp( + home: TDTextConfiguration( + globalFontFamily: FontFamily( + fontFamily: 'test1', + ), + child: Theme( + data: ThemeData(extensions: [themeData!]), + child: Builder( + builder: (context) { + + ScreenUtil.init(context); + return Scaffold( + appBar: _buildAppBar(context), + // appBar: _buildAppBar(context), + // body: StudyDetail(), + body: body(context), + bottomNavigationBar: _buildBottomTabBar(), + ); + }, + )), + ), + )); +} + +Padding body(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + + TDButton(text: "ixanshi ",onTap: (){ + TDLoadingController.show(context); + + TDLoadingController.dismiss(); + },), + // 先显示再加载 + TDText( + '测试文案', + textColor: TDTheme.of(context).brandNormalColor, + fontFamilyUrl: + 'https://xinyue.qq.com/m/flutter_web/assets/packages/flutter_component/fonts/FZLanTingHeiS-EB-GB.ttf', + fontFamily: FontFamily(fontFamily: 'test'), + ), + // // 先加载再显示 + // child: FutureBuilder( + // future:TDFontLoader.load(name: 'test1', fontFamilyUrl: 'https://xinyue.qq.com/m/flutter_web/assets/packages/flutter_component/fonts/FZLanTingHeiS-EB-GB.ttf'), + // initialData: false, + // builder: (_,data)=>TDText( + // (data.data ?? false) ? '测试文案' : '', + // textColor: TDTheme.of(context).brandNormalColor, + // fontFamilyUrl: 'https://xinyue.qq.com/m/flutter_web/assets/packages/flutter_component/fonts/FZLanTingHeiS-EB-GB.ttf', + // fontFamily: FontFamily(fontFamily: 'test1'), + // ), + // ), + TDInput( + // leftLabel: '标签文字', + // controller: controller[0], + type: TDInputType.cardStyle, + backgroundColor: Colors.white, + cardStyle: TDCardStyle.topTextWithBlueBorder, + hintText: '请输入文字', + cardStyleTopText: '标签文字', + // onChanged: (text) { + // setState(() {}); + // }, + // onClearTap: () { + // controller[0].clear(); + // setState(() {}); + // }, + ), + const SizedBox( + height: 16, + ), + const TDTextarea( + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + padding: EdgeInsets.zero, + indicator: true, + // backgroundColor: Colors.white, + // textInputBackgroundColor: Colors.white, + layout: TDTextareaLayout.vertical, + bordered: true, + ) + ], + ), + ); +} + +PreferredSizeWidget _buildAppBar(BuildContext context) { + return TDNavBar( + useDefaultBack: false, + // screenAdaptation: false, + flexibleSpace: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + colors: [ + Colors.red, Colors.green + ] + ) + ), + ), + // opacity: 0, + backgroundColor: Colors.red, + centerTitle: false, + titleMargin: 0, + titleWidget: TDSearchBar( + needCancel: false, + autoHeight: true, + backgroundColor: Colors.transparent, + padding: const EdgeInsets.fromLTRB(0, 2, 0, 2), + placeHolder: '搜索预设文案', + mediumStyle: true, + style: TDSearchStyle.round, + onTextChanged: (String text) { + print('input:$text'); + }, + ), + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home, iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis, iconSize: 24) + ]); +} + +TDBottomTabBar _buildBottomTabBar() { + var iconSize = 39 * 60 / 98; + var textSize = 8.0; + return TDBottomTabBar( + TDBottomTabBarBasicType.iconText, + componentType: TDBottomTabBarComponentType.normal, + useVerticalDivider: false, + barHeight: 98 * 60 / 98, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: Icon(TDIcons.home, size: iconSize, color: Colors.red), + unselectedIcon: Icon(TDIcons.home, size: iconSize, color: const Color(0xFF383838)), + tabText: '首页', + selectTabTextStyle: TextStyle(fontSize: textSize, color: Colors.red), + unselectTabTextStyle: TextStyle(fontSize: textSize, color: Colors.black), + onTap: () { + // context.read().changeIndex(0); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: Icon(TDIcons.app, size: iconSize, color: Colors.red), + unselectedIcon: Icon(TDIcons.app, size: iconSize, color: const Color(0xFF383838)), + tabText: '办事', + selectTabTextStyle: TextStyle(fontSize: textSize, color: Colors.red), + unselectTabTextStyle: TextStyle(fontSize: textSize, color: Colors.black), + onTap: () { + // context.read().changeIndex(1); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: Icon(TDIcons.user, size: iconSize, color: Colors.red), + unselectedIcon: Icon(TDIcons.user, size: iconSize, color: Color(0xFF383838)), + tabText: '我的', + selectTabTextStyle: TextStyle(fontSize: textSize, color: Colors.red), + unselectTabTextStyle: TextStyle(fontSize: textSize, color: Colors.black), + onTap: () { + // context.read().changeIndex(2); + }, + ), + ], + ); +} diff --git a/tdesign-component/example/lib/config.dart b/tdesign-component/example/lib/config.dart new file mode 100644 index 000000000..7bdf050ec --- /dev/null +++ b/tdesign-component/example/lib/config.dart @@ -0,0 +1,300 @@ +import 'package:flutter/material.dart'; + +import 'base/example_base.dart'; +import 'page/sidebar/td_sidebar_page.dart'; +import 'page/sidebar/td_sidebar_page_anchor.dart'; +import 'page/sidebar/td_sidebar_page_custom.dart'; +import 'page/sidebar/td_sidebar_page_icon.dart'; +import 'page/sidebar/td_sidebar_page_loading.dart'; +import 'page/sidebar/td_sidebar_page_outline.dart'; +import 'page/sidebar/td_sidebar_page_pagination.dart'; +import 'page/td_action_sheet_page.dart'; +import 'page/td_avatar_page.dart'; +import 'page/td_backtop_page.dart'; +import 'page/td_badge_page.dart'; +import 'page/td_bottom_tab_bar_page.dart'; +import 'page/td_button_page.dart'; +import 'page/td_calendar_page.dart'; +import 'page/td_cascader_page.dart'; +import 'page/td_cell_page.dart'; +import 'page/td_checkbox_page.dart'; +import 'page/td_collapse_page.dart'; +import 'page/td_date_picker_page.dart'; +import 'page/td_dialog_page.dart'; +import 'page/td_divider_page.dart'; +import 'page/td_drawer_page.dart'; +import 'page/td_dropdown_menu_page.dart'; +import 'page/td_empty_page.dart'; +import 'page/td_fab_page.dart'; +import 'page/td_font_page.dart'; +import 'page/td_footer_page.dart'; +import 'page/td_icon_page.dart'; +import 'page/td_image_page.dart'; +import 'page/td_image_viewer_page.dart'; +import 'page/td_indexes_page.dart'; +import 'page/td_input_page.dart'; +import 'page/td_form_page.dart'; +import 'page/td_link_page.dart'; +import 'page/td_loading_page.dart'; +import 'page/td_message_page.dart'; +import 'page/td_navbar_page.dart'; +import 'page/td_notice_bar_page.dart'; +import 'page/td_picker_page.dart'; +import 'page/td_popover_page.dart'; +import 'page/td_popup_page.dart'; +import 'page/td_progress_page.dart'; +import 'page/td_radio_page.dart'; +import 'page/td_radius_page.dart'; +import 'page/td_rate_page.dart'; +import 'page/td_refresh_page.dart'; +import 'page/td_result_page.dart'; +import 'page/td_search_bar_page.dart'; +import 'page/td_shadows_page.dart'; +import 'page/td_skeleton_page.dart'; +import 'page/td_slider_page.dart'; +import 'page/td_stepper_page.dart'; +import 'page/td_steps_page.dart'; +import 'page/td_swipe_cell_page.dart'; +import 'page/td_steps_page.dart'; +import 'page/td_swiper_page.dart'; +import 'page/td_switch_page.dart'; +import 'page/td_table_page.dart'; +import 'page/td_tabs_page.dart'; +import 'page/td_tag_page.dart'; +import 'page/td_text_page.dart'; +import 'page/td_textarea_page.dart'; +import 'page/td_theme_page.dart'; +import 'page/td_time_counter_page.dart'; +import 'page/td_toast_page.dart'; +import 'page/td_tree_select_page.dart'; +import 'page/td_upload_page.dart'; +import 'page/todo_page.dart'; + +PageBuilder _wrapInheritedTheme(WidgetBuilder builder) { + return (context, model) { + return ExamplePageInheritedTheme(model: model, child: builder(context)); + }; +} + +/// 新增的示例页面,在此增加模型即可,会自动注册增加按钮。示例页面编写参考TDTextPage() +List examplePageList = []; + +Map> exampleMap = { + '基础': [ + ExamplePageModel( + text: 'Button 按钮', name: 'button', pageBuilder: _wrapInheritedTheme((context) => const TDButtonPage())), + ExamplePageModel( + text: 'Divider 分割线', name: 'divider', pageBuilder: _wrapInheritedTheme((context) => const TDDividerPage())), + ExamplePageModel( + text: 'Fab 悬浮按钮', name: 'fab', pageBuilder: _wrapInheritedTheme((context) => const TDFabPage())), + ExamplePageModel(text: 'Icon 图标', name: 'icon', pageBuilder: _wrapInheritedTheme((context) => const TDIconPage())), + ExamplePageModel( + text: 'Link 链接', name: 'link', pageBuilder: _wrapInheritedTheme((context) => const TDLinkViewPage())), + ExamplePageModel(text: 'Text 文本', name: 'text', pageBuilder: _wrapInheritedTheme((context) => const TDTextPage())), + ], + '导航': [ + ExamplePageModel( + text: 'BackTop 返回顶部', + name: 'back-top', + pageName: 'backtop', + pageBuilder: _wrapInheritedTheme((context) => const TDBackTopPage())), + ExamplePageModel( + text: 'Drawer 抽屉', + name: 'drawer', + pageBuilder: _wrapInheritedTheme((context) => const TDDrawerPage())), + ExamplePageModel( + text: 'Indexes 索引', + name: 'indexes', + pageBuilder: _wrapInheritedTheme((context) => const TDIndexesPage())), + ExamplePageModel( + text: 'NavBar 导航栏', name: 'navbar', pageBuilder: _wrapInheritedTheme((context) => const TDNavBarPage())), + ExamplePageModel( + text: 'SideBar 侧边栏', name: 'side-bar',pageBuilder: _wrapInheritedTheme((context) => const TDSideBarPage())), + ExamplePageModel(text: 'Steps 步骤条', name: 'steps', pageBuilder: _wrapInheritedTheme((context) => const TDStepsPage())), + ExamplePageModel( + text: 'TabBar 标签栏', name: 'tab-bar', + pageName: 'bottom_tab_bar',pageBuilder: _wrapInheritedTheme((context) => const TDBottomTabBarPage())), + ExamplePageModel(text: 'Tabs 选项卡', name: 'tabs', pageBuilder: _wrapInheritedTheme((context) => const TDTabsPage())), + ], + '输入': [ + ExamplePageModel( + text: 'Calendar 日历', + name: 'calendar', + pageBuilder: _wrapInheritedTheme((context) => const TDCalendarPage())), + ExamplePageModel( + text: 'Cascader 级联选择器', + name: 'cascader', + pageBuilder: _wrapInheritedTheme((context) => const TDCascaderPage())), + ExamplePageModel( + text: 'Checkbox 多选框', name: 'checkbox', pageBuilder: _wrapInheritedTheme((context) => const TDCheckboxPage())), + ExamplePageModel( + text: 'DateTimePicker 时间选择器', + name: 'date-time-picker', + pageName: 'data_picker', + pageBuilder: _wrapInheritedTheme((context) => const TDDatePickerPage())), + ExamplePageModel( + text: 'Form 表单', name: 'form', pageBuilder: _wrapInheritedTheme((context) => const TDFormPage())), + ExamplePageModel( + text: 'Input 输入框', name: 'input', pageBuilder: _wrapInheritedTheme((context) => const TDInputViewPage())), + ExamplePageModel( + text: 'Picker 选择器', name: 'picker', pageBuilder: _wrapInheritedTheme((context) => const TDPickerPage())), + ExamplePageModel( + text: 'Radio 单选框', name: 'radio', pageBuilder: _wrapInheritedTheme((context) => const TDRadioPage())), + ExamplePageModel( + text: 'Rate 评分', name: 'rate', pageBuilder: _wrapInheritedTheme((context) => const TDRatePage())), + ExamplePageModel( + text: 'Search 搜索框', name: 'search', pageBuilder: _wrapInheritedTheme((context) => const TDSearchBarPage())), + ExamplePageModel( + text: 'Slider 滑动选择器', name: 'slider', pageBuilder: _wrapInheritedTheme((context) => const TDSliderPage())), + ExamplePageModel( + text: 'Stepper 步进器', name: 'stepper', pageBuilder: _wrapInheritedTheme((context) => const TDStepperPage())), + ExamplePageModel( + text: 'Switch 开关', name: 'switch', pageBuilder: _wrapInheritedTheme((context) => const TDSwitchPage())), + ExamplePageModel( + text: 'Textarea 多行文本框', + name: 'textarea', + pageBuilder: _wrapInheritedTheme((context) => const TDTextareaPage())), + ExamplePageModel( + text: 'TreeSelect 树形选择器', + name: 'tree-select', + pageName: 'tree_select', + pageBuilder: _wrapInheritedTheme((context) => const TDTreeSelectPage())), + ExamplePageModel( + text: 'Upload 上传', + name: 'upload', + pageBuilder: _wrapInheritedTheme((context) => const TDUploadPage())), + ], + '数据展示': [ + ExamplePageModel( + text: 'Avatar 头像', name: 'avatar', pageBuilder: _wrapInheritedTheme((context) => const TDAvatarPage())), + ExamplePageModel( + text: 'Badge 徽标', name: 'badge', pageBuilder: _wrapInheritedTheme((context) => const TDBadgePage())), + ExamplePageModel( + text: 'Cell 单元格', name: 'cell', pageBuilder: _wrapInheritedTheme((context) => const TDCellPage())), + ExamplePageModel( + text: 'TimeCounter 计时器', + name: 'time-counter', + pageBuilder: _wrapInheritedTheme((context) => const TDTimeCounterPage())), + ExamplePageModel( + text: 'Collapse 折叠面板', + name: 'collapse', + pageBuilder: _wrapInheritedTheme((context) => const TDCollapsePage())), + ExamplePageModel( + text: 'Empty 空状态', name: 'empty', pageBuilder: _wrapInheritedTheme((context) => const TDEmptyPage())), + ExamplePageModel( + text: 'Footer 页脚', name: 'footer', pageBuilder: _wrapInheritedTheme((context) => const TDFooterPage())), + ExamplePageModel( + text: 'Grid 宫格', name: 'grid', isTodo: true, pageBuilder: _wrapInheritedTheme((context) => const TodoPage())), + ExamplePageModel( + text: 'Image 图片', name: 'image', pageBuilder: _wrapInheritedTheme((context) => const TDImagePage())), + ExamplePageModel( + text: 'ImageViewer 图片预览', + name: 'image-viewer', + pageName: 'image_viewer', + pageBuilder: _wrapInheritedTheme((context) => const TDImageViewerPage())), + ExamplePageModel( + text: 'Progress 进度条', + name: 'progress', + pageBuilder: _wrapInheritedTheme((context) => const TDProgressPage())), + ExamplePageModel( + text: 'Result 结果', + name: 'result', + pageBuilder: _wrapInheritedTheme((context) => const TDResultPage())), + ExamplePageModel( + text: 'Skeleton 骨架屏', + name: 'skeleton', + pageBuilder: _wrapInheritedTheme((context) => const TDSkeletonPage())), + ExamplePageModel( + text: 'Sticky 吸顶', + name: 'sticky', + isTodo: true, + pageBuilder: _wrapInheritedTheme((context) => const TodoPage())), + ExamplePageModel( + text: 'Swiper 轮播图', name: 'swiper', pageBuilder: _wrapInheritedTheme((context) => const TDSwiperPage())), + ExamplePageModel(text: 'Table 表格', name: 'table', pageBuilder: _wrapInheritedTheme((context) => const TDTablePage())), + ExamplePageModel(text: 'Tag 标签', name: 'tag', pageBuilder: _wrapInheritedTheme((context) => const TDTagPage())), + ], + '反馈': [ + ExamplePageModel( + text: 'ActionSheet 动作面板', + name: 'action-sheet', + pageName: 'action_sheet', + pageBuilder: _wrapInheritedTheme((context) => const TDActionSheetPage())), + ExamplePageModel( + text: 'Dialog 对话框', name: 'dialog', pageBuilder: _wrapInheritedTheme((context) => const TDDialogPage())), + ExamplePageModel( + text: 'DropdownMenu 下拉菜单', + name: 'dropdown-menu', + pageName: 'dropdown_menu', + pageBuilder: _wrapInheritedTheme((context) => const TDDropdownMenuPage())), + ExamplePageModel( + text: 'Loading 加载', name: 'loading', pageBuilder: _wrapInheritedTheme((context) => const TDLoadingPage())), + ExamplePageModel( + text: 'Message 全局提示', + name: 'message', + pageBuilder: _wrapInheritedTheme((context) => const TDMessagePage())), + ExamplePageModel( + text: 'NoticeBar 消息提醒', name: 'notice-bar', pageBuilder: _wrapInheritedTheme((context) => const TDNoticeBarPage())), + ExamplePageModel( + text: 'Overlay 遮罩层', + name: 'overlay', + isTodo: true, + pageBuilder: _wrapInheritedTheme((context) => const TodoPage())), + ExamplePageModel(text: 'Popover 弹出气泡', name: 'popover', pageBuilder: _wrapInheritedTheme((context) => const TDPopoverPage())), + ExamplePageModel( + text: 'Popup 弹出层', name: 'popup', pageBuilder: _wrapInheritedTheme((context) => const TDPopupPage())), + ExamplePageModel( + text: 'PullDownRefresh 下拉刷新', + name: 'pull-down-refresh', + pageName: 'refresh', + pageBuilder: _wrapInheritedTheme((context) => const TdPullDownRefreshPage())), + ExamplePageModel( + text: 'Swipecell 滑动操作', + name: 'swipe-cell', + pageName: 'swipe_cell', + pageBuilder: _wrapInheritedTheme((context) => const TDSwipeCellPage())), + ExamplePageModel( + text: 'Toast 轻提示', name: 'toast', pageBuilder: _wrapInheritedTheme((context) => const TDToastPage())), + ], + '主题': [ + ExamplePageModel( + text: '颜色', name: 'theme_colors', pageBuilder: _wrapInheritedTheme((context) => const TDThemeColorsPage())), + ExamplePageModel(text: '字体', name: 'font', pageBuilder: _wrapInheritedTheme((context) => const TDFontPage())), + ExamplePageModel(text: '圆角', name: 'radius', pageBuilder: _wrapInheritedTheme((context) => const TDRadiusPage())), + ExamplePageModel(text: '阴影', name: 'shadows', pageBuilder: _wrapInheritedTheme((context) => const TDShadowsPage())), + ], +}; + +List sideBarExamplePage = [ + ExamplePageModel( + text: 'SideBar 切页', + name: 'SideBarPagination', + isTodo: false, + showAction: false, + pageBuilder: _wrapInheritedTheme((context) => const TDSideBarPaginationPage())), + ExamplePageModel( + text: 'SideBar 锚点', + name: 'SideBarAnchor', + isTodo: false, + pageBuilder: _wrapInheritedTheme((context) => const TDSideBarAnchorPage())), + ExamplePageModel( + text: 'SideBar 带图标', + name: 'SideBarIcon', + isTodo: false, + pageBuilder: _wrapInheritedTheme((context) => const TDSideBarIconPage())), + ExamplePageModel( + text: 'SideBar 非通栏选项样式', + name: 'SideBarOutline', + isTodo: false, + pageBuilder: _wrapInheritedTheme((context) => const TDSideBarOutlinePage())), + ExamplePageModel( + text: 'SideBar 自定义样式', + name: 'SideBarCustom', + isTodo: false, + pageBuilder: _wrapInheritedTheme((context) => const TDSideBarCustomPage())), + ExamplePageModel( + text: 'SideBar 延迟加载', + name: 'SideBarLoading', + isTodo: false, + pageBuilder: _wrapInheritedTheme((context) => const TDSideBarLoadingPage())) +]; diff --git a/tdesign-component/example/lib/home.dart b/tdesign-component/example/lib/home.dart new file mode 100644 index 000000000..76003cfcb --- /dev/null +++ b/tdesign-component/example/lib/home.dart @@ -0,0 +1,251 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import 'base/example_base.dart'; +import 'base/example_route.dart'; +import 'base/web_md_tool.dart'; +import 'config.dart'; +import 'localizations/app_localizations.dart'; + +var _kShowTodoComponent = false; + +/// 切换主题的回调 +typedef OnThemeChange = Function(TDThemeData themeData); + +/// 切换语言的回调 +typedef OnLocaleChange = Function(Locale locale); + +/// 示例首页 +class MyHomePage extends StatefulWidget { + const MyHomePage({Key? key, required this.title, this.onThemeChange, this.locale, this.onLocaleChange,}) : super(key: key); + + final String title; + + final OnThemeChange? onThemeChange; + + final OnLocaleChange? onLocaleChange; + + final Locale? locale; + + @override + _MyHomePageState createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + bool useConch = false; + String searchText = ''; + FocusNode focusNode = FocusNode(); + + @override + void initState() { + super.initState(); + TDExampleRoute.init(); + sideBarExamplePage.forEach(TDExampleRoute.add); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + + return Scaffold( + appBar: AppBar( + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + backgroundColor: TDTheme.of(context).brandNormalColor, + titleTextStyle: TextStyle(color:TDTheme.of(context).whiteColor1, fontSize: TDTheme.of(context).fontTitleLarge?.size), + title: Text(widget.title), + actions: ScreenUtil.isWebLargeScreen(context) + ? null + : [ + + GestureDetector( + child: Container( + alignment: Alignment.centerRight, + padding: const EdgeInsets.only( + right: 16, + ), + child: TDText( + widget.locale?.languageCode == 'en' ? '中文' : 'English', + textColor: TDTheme.of(context).whiteColor1, + ), + ), + onTap: () { + if(widget.locale?.languageCode == 'en') { + widget.onLocaleChange?.call(const Locale('zh')); + } else { + widget.onLocaleChange?.call(const Locale('en')); + } + }, + ), + GestureDetector( + child: Container( + alignment: Alignment.centerRight, + padding: const EdgeInsets.only( + right: 16, + ), + child: TDText( + AppLocalizations.of(context)?.about, + textColor: TDTheme.of(context).whiteColor1, + ), + ), + onTap: () { + focusNode.unfocus(); + Navigator.pushNamed(context, TDExampleRoute.aboutPath); + }, + ) + ], + ), + body: _buildBody(context), + ); + } + + Widget _buildBody(BuildContext context) { + return SafeArea( + child: Align( + alignment: Alignment.topCenter, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildChildren(context), + ), + ), + )); + } + + List _buildChildren(BuildContext context) { + var children = []; + + // 添加切换主题的按钮 + children.add(Padding( + padding: const EdgeInsets.only(top: 16), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Container( + constraints: BoxConstraints(minWidth: MediaQuery.of(context).size.width), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Padding(padding: const EdgeInsets.only(left: 8, right: 4),child: TDTheme( + data: TDThemeData.defaultData(), + child: TDButton( + text: AppLocalizations.of(context)?.defaultTheme, + theme: TDButtonTheme.primary, + onTap: () { + widget.onThemeChange?.call(TDTheme.defaultData()); + }, + )),), + Padding(padding: const EdgeInsets.only(left: 4, right: 4),child: TDTheme( + data: TDThemeData.fromJson('green', greenThemeConfig) ?? TDThemeData.defaultData(), + child: TDButton( + text: AppLocalizations.of(context)?.greenTheme, + theme: TDButtonTheme.primary, + onTap: () async { + var jsonString = await rootBundle.loadString('assets/theme.json'); + var newData = TDThemeData.fromJson('green', jsonString); + widget.onThemeChange?.call(newData ?? TDTheme.defaultData()); + }))), + Padding(padding: const EdgeInsets.only(left: 4, right: 8),child: TDTheme( + data: TDThemeData.fromJson('red', greenThemeConfig) ?? TDThemeData.defaultData(), + child: TDButton( + text: AppLocalizations.of(context)?.redTheme, + theme: TDButtonTheme.danger, + onTap: () async { + var jsonString = await rootBundle.loadString('assets/theme.json'); + var newData = TDThemeData.fromJson('red', jsonString); + widget.onThemeChange?.call(newData ?? TDTheme.defaultData()); + }))), + ], + ), + ), + ), + )); + + children.add(TDSearchBar( + placeHolder: '请输入组件名称', + focusNode: focusNode, + onTextChanged: (value){ + setState(() { + searchText = value; + }); + }, + )); + + exampleMap.forEach((key, value) { + var subList = []; + value.forEach((model) { + if(searchText.isNotEmpty && !model.text.toLowerCase().contains(searchText.toLowerCase())){ + // 如果有搜索文案,不再搜索中的组件不展示 + return; + } + model.spline = WebMdTool.getSpline(key); + if (model.isTodo) { + if (_kShowTodoComponent) { + children.add(Padding( + padding: const EdgeInsets.only(left: 40, right: 40, top: 8, bottom: 8), + child: TDButton( + size: TDButtonSize.medium, + type: TDButtonType.outline, + shape: TDButtonShape.filled, + theme: TDButtonTheme.defaultTheme, + textStyle: TextStyle(color: TDTheme.of(context).fontGyColor4), + onTap: () { + Navigator.pushNamed(context, '${model.name}?showAction=1'); + }, + text: model.text), + )); + } + } else { + subList.add(Padding( + padding: const EdgeInsets.only(left: 40, right: 40, top: 8, bottom: 8), + child: TDButton( + size: TDButtonSize.medium, + type: TDButtonType.outline, + shape: TDButtonShape.filled, + theme: TDButtonTheme.primary, + onTap: () { + focusNode.unfocus(); + Navigator.pushNamed(context, '${model.name}?showAction=1'); + }, + text: model.text), + )); + } + }); + children.add(Container( + alignment: Alignment.topLeft, + margin: const EdgeInsets.only(left: 16, right: 16, top: 16), + padding: const EdgeInsets.only(left: 12), + decoration: BoxDecoration( + color: TDTheme.of(context).brandHoverColor, + borderRadius: BorderRadius.only(topRight: Radius.circular(TDTheme.of(context).radiusLarge))), + child: TDText( + '$key(${subList.length})', + textColor: TDTheme.of(context).whiteColor1, + ), + )); + children.addAll(subList); + }); + return children; + } +} + + +String greenThemeConfig = ''' + { + "green": { + "color": { + "brandNormalColor": "#45c58b" + } + }, + "red": { + "color": { + "brandNormalColor": "#ff0000" + } + } +} + '''; diff --git a/tdesign-component/example/lib/l10n/app_en.arb b/tdesign-component/example/lib/l10n/app_en.arb new file mode 100644 index 000000000..c67879ea5 --- /dev/null +++ b/tdesign-component/example/lib/l10n/app_en.arb @@ -0,0 +1,60 @@ +{ + "components": "TD Flutter Components", + "about": "About", + "defaultTheme": "defaultTheme", + "greenTheme": "greenTheme", + "redTheme": "redTheme", + "cancel": "Cancel", + "confirm": "Confirm", + "switchClose": "C", + "switchOpen": "O", + "knew": "Knew", + "loading": "Loading", + "loadingWithPoint": "Loading...", + "other": "Other", + "refreshing": "Refreshing", + "releaseRefresh": "ReleaseRefresh", + "pullToRefresh": "PullToRefresh", + "completeRefresh": "CompleteRefresh", + "reset": "Reset", + "days": "days", + "hours": "hours", + "minutes": "minutes", + "seconds": "seconds", + "milliseconds": "milliseconds", + "yearLabel": "year", + "monthLabel": "month", + "dateLabel": "date", + "weeks":"weeks", + "sunday": "SUN", + "monday": "MON", + "tuesday": "TUE", + "wednesday": "WED", + "thursday": "THU", + "friday": "FRI", + "saturday": "SAT", + "year": "", + "january": "January", + "february": "February", + "march": "March", + "april": "April", + "may": "May", + "june": "June", + "july": "July", + "august": "August", + "september": "September", + "october": "October", + "november": "November", + "december": "December", + "time": "Time", + "start": "Start", + "end": "End", + "notRated": "Not rated", + "cascadeLabel": "Select Item", + "yearLabel": "year", + "monthLabel": "month", + "dateLabel": "date", + "weeks":"weeks", + "back":"BACK", + "top":"TOP" +} \ No newline at end of file diff --git a/tdesign-component/example/lib/l10n/app_localizations.dart b/tdesign-component/example/lib/l10n/app_localizations.dart new file mode 100644 index 000000000..fb3b78236 --- /dev/null +++ b/tdesign-component/example/lib/l10n/app_localizations.dart @@ -0,0 +1,457 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_en.dart'; +import 'app_localizations_zh.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations? of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('en'), + Locale('zh') + ]; + + /// No description provided for @components. + /// + /// In en, this message translates to: + /// **'TD Flutter Components'** + String get components; + + /// No description provided for @about. + /// + /// In en, this message translates to: + /// **'About'** + String get about; + + /// No description provided for @defaultTheme. + /// + /// In en, this message translates to: + /// **'defaultTheme'** + String get defaultTheme; + + /// No description provided for @greenTheme. + /// + /// In en, this message translates to: + /// **'greenTheme'** + String get greenTheme; + + /// No description provided for @redTheme. + /// + /// In en, this message translates to: + /// **'redTheme'** + String get redTheme; + + /// No description provided for @cancel. + /// + /// In en, this message translates to: + /// **'Cancel'** + String get cancel; + + /// No description provided for @confirm. + /// + /// In en, this message translates to: + /// **'Confirm'** + String get confirm; + + /// No description provided for @switchClose. + /// + /// In en, this message translates to: + /// **'C'** + String get switchClose; + + /// No description provided for @switchOpen. + /// + /// In en, this message translates to: + /// **'O'** + String get switchOpen; + + /// No description provided for @knew. + /// + /// In en, this message translates to: + /// **'Knew'** + String get knew; + + /// No description provided for @loading. + /// + /// In en, this message translates to: + /// **'Loading'** + String get loading; + + /// No description provided for @loadingWithPoint. + /// + /// In en, this message translates to: + /// **'Loading...'** + String get loadingWithPoint; + + /// No description provided for @other. + /// + /// In en, this message translates to: + /// **'Other'** + String get other; + + /// No description provided for @refreshing. + /// + /// In en, this message translates to: + /// **'Refreshing'** + String get refreshing; + + /// No description provided for @releaseRefresh. + /// + /// In en, this message translates to: + /// **'ReleaseRefresh'** + String get releaseRefresh; + + /// No description provided for @pullToRefresh. + /// + /// In en, this message translates to: + /// **'PullToRefresh'** + String get pullToRefresh; + + /// No description provided for @completeRefresh. + /// + /// In en, this message translates to: + /// **'CompleteRefresh'** + String get completeRefresh; + + /// No description provided for @reset. + /// + /// In en, this message translates to: + /// **'Reset'** + String get reset; + + /// No description provided for @days. + /// + /// In en, this message translates to: + /// **'days'** + String get days; + + /// No description provided for @hours. + /// + /// In en, this message translates to: + /// **'hours'** + String get hours; + + /// No description provided for @minutes. + /// + /// In en, this message translates to: + /// **'minutes'** + String get minutes; + + /// No description provided for @seconds. + /// + /// In en, this message translates to: + /// **'seconds'** + String get seconds; + + /// No description provided for @milliseconds. + /// + /// In en, this message translates to: + /// **'milliseconds'** + String get milliseconds; + + /// No description provided for @yearLabel. + /// + /// In en, this message translates to: + /// **'year'** + String get yearLabel; + + /// No description provided for @monthLabel. + /// + /// In en, this message translates to: + /// **'month'** + String get monthLabel; + + /// No description provided for @dateLabel. + /// + /// In en, this message translates to: + /// **'date'** + String get dateLabel; + + /// No description provided for @weeks. + /// + /// In en, this message translates to: + /// **'weeks'** + String get weeks; + + /// No description provided for @sunday. + /// + /// In en, this message translates to: + /// **'SUN'** + String get sunday; + + /// No description provided for @monday. + /// + /// In en, this message translates to: + /// **'MON'** + String get monday; + + /// No description provided for @tuesday. + /// + /// In en, this message translates to: + /// **'TUE'** + String get tuesday; + + /// No description provided for @wednesday. + /// + /// In en, this message translates to: + /// **'WED'** + String get wednesday; + + /// No description provided for @thursday. + /// + /// In en, this message translates to: + /// **'THU'** + String get thursday; + + /// No description provided for @friday. + /// + /// In en, this message translates to: + /// **'FRI'** + String get friday; + + /// No description provided for @saturday. + /// + /// In en, this message translates to: + /// **'SAT'** + String get saturday; + + /// No description provided for @year. + /// + /// In en, this message translates to: + /// **''** + String get year; + + /// No description provided for @january. + /// + /// In en, this message translates to: + /// **'January'** + String get january; + + /// No description provided for @february. + /// + /// In en, this message translates to: + /// **'February'** + String get february; + + /// No description provided for @march. + /// + /// In en, this message translates to: + /// **'March'** + String get march; + + /// No description provided for @april. + /// + /// In en, this message translates to: + /// **'April'** + String get april; + + /// No description provided for @may. + /// + /// In en, this message translates to: + /// **'May'** + String get may; + + /// No description provided for @june. + /// + /// In en, this message translates to: + /// **'June'** + String get june; + + /// No description provided for @july. + /// + /// In en, this message translates to: + /// **'July'** + String get july; + + /// No description provided for @august. + /// + /// In en, this message translates to: + /// **'August'** + String get august; + + /// No description provided for @september. + /// + /// In en, this message translates to: + /// **'September'** + String get september; + + /// No description provided for @october. + /// + /// In en, this message translates to: + /// **'October'** + String get october; + + /// No description provided for @november. + /// + /// In en, this message translates to: + /// **'November'** + String get november; + + /// No description provided for @december. + /// + /// In en, this message translates to: + /// **'December'** + String get december; + + /// No description provided for @time. + /// + /// In en, this message translates to: + /// **'Time'** + String get time; + + /// No description provided for @start. + /// + /// In en, this message translates to: + /// **'Start'** + String get start; + + /// No description provided for @end. + /// + /// In en, this message translates to: + /// **'End'** + String get end; + + /// No description provided for @notRated. + /// + /// In en, this message translates to: + /// **'Not rated'** + String get notRated; + + /// No description provided for @cascadeLabel. + /// + /// In en, this message translates to: + /// **'Select Item'** + String get cascadeLabel; + + /// No description provided for @back. + /// + /// In en, this message translates to: + /// **'BACK'** + String get back; + + /// No description provided for @top. + /// + /// In en, this message translates to: + /// **'TOP'** + String get top; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en', 'zh'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return AppLocalizationsEn(); + case 'zh': + return AppLocalizationsZh(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); +} diff --git a/tdesign-component/example/lib/l10n/app_localizations_en.dart b/tdesign-component/example/lib/l10n/app_localizations_en.dart new file mode 100644 index 000000000..28c285fc6 --- /dev/null +++ b/tdesign-component/example/lib/l10n/app_localizations_en.dart @@ -0,0 +1,172 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get components => 'TD Flutter Components'; + + @override + String get about => 'About'; + + @override + String get defaultTheme => 'defaultTheme'; + + @override + String get greenTheme => 'greenTheme'; + + @override + String get redTheme => 'redTheme'; + + @override + String get cancel => 'Cancel'; + + @override + String get confirm => 'Confirm'; + + @override + String get switchClose => 'C'; + + @override + String get switchOpen => 'O'; + + @override + String get knew => 'Knew'; + + @override + String get loading => 'Loading'; + + @override + String get loadingWithPoint => 'Loading...'; + + @override + String get other => 'Other'; + + @override + String get refreshing => 'Refreshing'; + + @override + String get releaseRefresh => 'ReleaseRefresh'; + + @override + String get pullToRefresh => 'PullToRefresh'; + + @override + String get completeRefresh => 'CompleteRefresh'; + + @override + String get reset => 'Reset'; + + @override + String get days => 'days'; + + @override + String get hours => 'hours'; + + @override + String get minutes => 'minutes'; + + @override + String get seconds => 'seconds'; + + @override + String get milliseconds => 'milliseconds'; + + @override + String get yearLabel => 'year'; + + @override + String get monthLabel => 'month'; + + @override + String get dateLabel => 'date'; + + @override + String get weeks => 'weeks'; + + @override + String get sunday => 'SUN'; + + @override + String get monday => 'MON'; + + @override + String get tuesday => 'TUE'; + + @override + String get wednesday => 'WED'; + + @override + String get thursday => 'THU'; + + @override + String get friday => 'FRI'; + + @override + String get saturday => 'SAT'; + + @override + String get year => ''; + + @override + String get january => 'January'; + + @override + String get february => 'February'; + + @override + String get march => 'March'; + + @override + String get april => 'April'; + + @override + String get may => 'May'; + + @override + String get june => 'June'; + + @override + String get july => 'July'; + + @override + String get august => 'August'; + + @override + String get september => 'September'; + + @override + String get october => 'October'; + + @override + String get november => 'November'; + + @override + String get december => 'December'; + + @override + String get time => 'Time'; + + @override + String get start => 'Start'; + + @override + String get end => 'End'; + + @override + String get notRated => 'Not rated'; + + @override + String get cascadeLabel => 'Select Item'; + + @override + String get back => 'BACK'; + + @override + String get top => 'TOP'; +} diff --git a/tdesign-component/example/lib/l10n/app_localizations_zh.dart b/tdesign-component/example/lib/l10n/app_localizations_zh.dart new file mode 100644 index 000000000..ec680e409 --- /dev/null +++ b/tdesign-component/example/lib/l10n/app_localizations_zh.dart @@ -0,0 +1,172 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Chinese (`zh`). +class AppLocalizationsZh extends AppLocalizations { + AppLocalizationsZh([String locale = 'zh']) : super(locale); + + @override + String get components => 'TDesign Flutter 组件库'; + + @override + String get about => '关于'; + + @override + String get defaultTheme => '默认主题'; + + @override + String get greenTheme => '绿色主题'; + + @override + String get redTheme => '红色主题'; + + @override + String get cancel => '取消'; + + @override + String get confirm => '确认'; + + @override + String get switchClose => '关'; + + @override + String get switchOpen => '开'; + + @override + String get knew => '知道了'; + + @override + String get loading => '加载中'; + + @override + String get loadingWithPoint => '加载中...'; + + @override + String get other => '其它'; + + @override + String get refreshing => '正在刷新'; + + @override + String get releaseRefresh => '松开刷新'; + + @override + String get pullToRefresh => '下拉刷新'; + + @override + String get completeRefresh => '刷新完成'; + + @override + String get reset => '重置'; + + @override + String get days => '天'; + + @override + String get hours => '时'; + + @override + String get minutes => '分'; + + @override + String get seconds => '秒'; + + @override + String get milliseconds => '毫秒'; + + @override + String get yearLabel => '年'; + + @override + String get monthLabel => '月'; + + @override + String get dateLabel => '日'; + + @override + String get weeks => '周'; + + @override + String get sunday => '日'; + + @override + String get monday => '一'; + + @override + String get tuesday => '二'; + + @override + String get wednesday => '三'; + + @override + String get thursday => '四'; + + @override + String get friday => '五'; + + @override + String get saturday => '六'; + + @override + String get year => ' 年'; + + @override + String get january => '1 月'; + + @override + String get february => '2 月'; + + @override + String get march => '3 月'; + + @override + String get april => '4 月'; + + @override + String get may => '5 月'; + + @override + String get june => '6 月'; + + @override + String get july => '7 月'; + + @override + String get august => '8 月'; + + @override + String get september => '9 月'; + + @override + String get october => '10 月'; + + @override + String get november => '11 月'; + + @override + String get december => '12 月'; + + @override + String get time => '时间'; + + @override + String get start => '开始'; + + @override + String get end => '结束'; + + @override + String get notRated => '未评分'; + + @override + String get cascadeLabel => '选择选项'; + + @override + String get back => '返回'; + + @override + String get top => '顶部'; +} diff --git a/tdesign-component/example/lib/l10n/app_zh.arb b/tdesign-component/example/lib/l10n/app_zh.arb new file mode 100644 index 000000000..70291ef8c --- /dev/null +++ b/tdesign-component/example/lib/l10n/app_zh.arb @@ -0,0 +1,60 @@ +{ + "components": "TDesign Flutter 组件库", + "about": "关于", + "defaultTheme": "默认主题", + "greenTheme": "绿色主题", + "redTheme": "红色主题", + "cancel": "取消", + "confirm": "确认", + "switchClose": "关", + "switchOpen": "开", + "knew": "知道了", + "loading": "加载中", + "loadingWithPoint": "加载中...", + "other": "其它", + "refreshing": "正在刷新", + "releaseRefresh": "松开刷新", + "pullToRefresh": "下拉刷新", + "completeRefresh": "刷新完成", + "reset": "重置", + "days": "天", + "hours": "时", + "minutes": "分", + "seconds": "秒", + "milliseconds": "毫秒", + "yearLabel":"年", + "monthLabel": "月", + "dateLabel": "日", + "weeks":"周", + "sunday": "日", + "monday": "一", + "tuesday": "二", + "wednesday": "三", + "thursday": "四", + "friday": "五", + "saturday": "六", + "year": " 年", + "january": "1 月", + "february": "2 月", + "march": "3 月", + "april": "4 月", + "may": "5 月", + "june": "6 月", + "july": "7 月", + "august": "8 月", + "september": "9 月", + "october": "10 月", + "november": "11 月", + "december": "12 月", + "time": "时间", + "start": "开始", + "end": "结束", + "notRated": "未评分", + "cascadeLabel": "选择选项", + "yearLabel":"年", + "monthLabel": "月", + "dateLabel": "日", + "weeks":"周", + "back":"返回", + "top":"顶部" +} \ No newline at end of file diff --git a/tdesign-component/example/lib/localizations/app_localizations.dart b/tdesign-component/example/lib/localizations/app_localizations.dart new file mode 100644 index 000000000..325ca5287 --- /dev/null +++ b/tdesign-component/example/lib/localizations/app_localizations.dart @@ -0,0 +1,457 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_en.dart'; +import '../localizations/app_localizations_zh.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations? of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('en'), + Locale('zh') + ]; + + /// No description provided for @components. + /// + /// In en, this message translates to: + /// **'TD Flutter Components'** + String get components; + + /// No description provided for @about. + /// + /// In en, this message translates to: + /// **'About'** + String get about; + + /// No description provided for @defaultTheme. + /// + /// In en, this message translates to: + /// **'defaultTheme'** + String get defaultTheme; + + /// No description provided for @greenTheme. + /// + /// In en, this message translates to: + /// **'greenTheme'** + String get greenTheme; + + /// No description provided for @redTheme. + /// + /// In en, this message translates to: + /// **'redTheme'** + String get redTheme; + + /// No description provided for @cancel. + /// + /// In en, this message translates to: + /// **'Cancel'** + String get cancel; + + /// No description provided for @confirm. + /// + /// In en, this message translates to: + /// **'Confirm'** + String get confirm; + + /// No description provided for @switchClose. + /// + /// In en, this message translates to: + /// **'C'** + String get switchClose; + + /// No description provided for @switchOpen. + /// + /// In en, this message translates to: + /// **'O'** + String get switchOpen; + + /// No description provided for @knew. + /// + /// In en, this message translates to: + /// **'Knew'** + String get knew; + + /// No description provided for @loading. + /// + /// In en, this message translates to: + /// **'Loading'** + String get loading; + + /// No description provided for @loadingWithPoint. + /// + /// In en, this message translates to: + /// **'Loading...'** + String get loadingWithPoint; + + /// No description provided for @other. + /// + /// In en, this message translates to: + /// **'Other'** + String get other; + + /// No description provided for @refreshing. + /// + /// In en, this message translates to: + /// **'Refreshing'** + String get refreshing; + + /// No description provided for @releaseRefresh. + /// + /// In en, this message translates to: + /// **'ReleaseRefresh'** + String get releaseRefresh; + + /// No description provided for @pullToRefresh. + /// + /// In en, this message translates to: + /// **'PullToRefresh'** + String get pullToRefresh; + + /// No description provided for @completeRefresh. + /// + /// In en, this message translates to: + /// **'CompleteRefresh'** + String get completeRefresh; + + /// No description provided for @reset. + /// + /// In en, this message translates to: + /// **'Reset'** + String get reset; + + /// No description provided for @days. + /// + /// In en, this message translates to: + /// **'days'** + String get days; + + /// No description provided for @hours. + /// + /// In en, this message translates to: + /// **'hours'** + String get hours; + + /// No description provided for @minutes. + /// + /// In en, this message translates to: + /// **'minutes'** + String get minutes; + + /// No description provided for @seconds. + /// + /// In en, this message translates to: + /// **'seconds'** + String get seconds; + + /// No description provided for @milliseconds. + /// + /// In en, this message translates to: + /// **'milliseconds'** + String get milliseconds; + + /// No description provided for @yearLabel. + /// + /// In en, this message translates to: + /// **'year'** + String get yearLabel; + + /// No description provided for @monthLabel. + /// + /// In en, this message translates to: + /// **'month'** + String get monthLabel; + + /// No description provided for @dateLabel. + /// + /// In en, this message translates to: + /// **'date'** + String get dateLabel; + + /// No description provided for @weeks. + /// + /// In en, this message translates to: + /// **'weeks'** + String get weeks; + + /// No description provided for @sunday. + /// + /// In en, this message translates to: + /// **'SUN'** + String get sunday; + + /// No description provided for @monday. + /// + /// In en, this message translates to: + /// **'MON'** + String get monday; + + /// No description provided for @tuesday. + /// + /// In en, this message translates to: + /// **'TUE'** + String get tuesday; + + /// No description provided for @wednesday. + /// + /// In en, this message translates to: + /// **'WED'** + String get wednesday; + + /// No description provided for @thursday. + /// + /// In en, this message translates to: + /// **'THU'** + String get thursday; + + /// No description provided for @friday. + /// + /// In en, this message translates to: + /// **'FRI'** + String get friday; + + /// No description provided for @saturday. + /// + /// In en, this message translates to: + /// **'SAT'** + String get saturday; + + /// No description provided for @year. + /// + /// In en, this message translates to: + /// **''** + String get year; + + /// No description provided for @january. + /// + /// In en, this message translates to: + /// **'January'** + String get january; + + /// No description provided for @february. + /// + /// In en, this message translates to: + /// **'February'** + String get february; + + /// No description provided for @march. + /// + /// In en, this message translates to: + /// **'March'** + String get march; + + /// No description provided for @april. + /// + /// In en, this message translates to: + /// **'April'** + String get april; + + /// No description provided for @may. + /// + /// In en, this message translates to: + /// **'May'** + String get may; + + /// No description provided for @june. + /// + /// In en, this message translates to: + /// **'June'** + String get june; + + /// No description provided for @july. + /// + /// In en, this message translates to: + /// **'July'** + String get july; + + /// No description provided for @august. + /// + /// In en, this message translates to: + /// **'August'** + String get august; + + /// No description provided for @september. + /// + /// In en, this message translates to: + /// **'September'** + String get september; + + /// No description provided for @october. + /// + /// In en, this message translates to: + /// **'October'** + String get october; + + /// No description provided for @november. + /// + /// In en, this message translates to: + /// **'November'** + String get november; + + /// No description provided for @december. + /// + /// In en, this message translates to: + /// **'December'** + String get december; + + /// No description provided for @time. + /// + /// In en, this message translates to: + /// **'Time'** + String get time; + + /// No description provided for @start. + /// + /// In en, this message translates to: + /// **'Start'** + String get start; + + /// No description provided for @end. + /// + /// In en, this message translates to: + /// **'End'** + String get end; + + /// No description provided for @notRated. + /// + /// In en, this message translates to: + /// **'Not rated'** + String get notRated; + + /// No description provided for @cascadeLabel. + /// + /// In en, this message translates to: + /// **'Select Item'** + String get cascadeLabel; + + /// No description provided for @back. + /// + /// In en, this message translates to: + /// **'BACK'** + String get back; + + /// No description provided for @top. + /// + /// In en, this message translates to: + /// **'TOP'** + String get top; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en', 'zh'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return AppLocalizationsEn(); + case 'zh': + return AppLocalizationsZh(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); +} diff --git a/tdesign-component/example/lib/localizations/app_localizations_en.dart b/tdesign-component/example/lib/localizations/app_localizations_en.dart new file mode 100644 index 000000000..28c285fc6 --- /dev/null +++ b/tdesign-component/example/lib/localizations/app_localizations_en.dart @@ -0,0 +1,172 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get components => 'TD Flutter Components'; + + @override + String get about => 'About'; + + @override + String get defaultTheme => 'defaultTheme'; + + @override + String get greenTheme => 'greenTheme'; + + @override + String get redTheme => 'redTheme'; + + @override + String get cancel => 'Cancel'; + + @override + String get confirm => 'Confirm'; + + @override + String get switchClose => 'C'; + + @override + String get switchOpen => 'O'; + + @override + String get knew => 'Knew'; + + @override + String get loading => 'Loading'; + + @override + String get loadingWithPoint => 'Loading...'; + + @override + String get other => 'Other'; + + @override + String get refreshing => 'Refreshing'; + + @override + String get releaseRefresh => 'ReleaseRefresh'; + + @override + String get pullToRefresh => 'PullToRefresh'; + + @override + String get completeRefresh => 'CompleteRefresh'; + + @override + String get reset => 'Reset'; + + @override + String get days => 'days'; + + @override + String get hours => 'hours'; + + @override + String get minutes => 'minutes'; + + @override + String get seconds => 'seconds'; + + @override + String get milliseconds => 'milliseconds'; + + @override + String get yearLabel => 'year'; + + @override + String get monthLabel => 'month'; + + @override + String get dateLabel => 'date'; + + @override + String get weeks => 'weeks'; + + @override + String get sunday => 'SUN'; + + @override + String get monday => 'MON'; + + @override + String get tuesday => 'TUE'; + + @override + String get wednesday => 'WED'; + + @override + String get thursday => 'THU'; + + @override + String get friday => 'FRI'; + + @override + String get saturday => 'SAT'; + + @override + String get year => ''; + + @override + String get january => 'January'; + + @override + String get february => 'February'; + + @override + String get march => 'March'; + + @override + String get april => 'April'; + + @override + String get may => 'May'; + + @override + String get june => 'June'; + + @override + String get july => 'July'; + + @override + String get august => 'August'; + + @override + String get september => 'September'; + + @override + String get october => 'October'; + + @override + String get november => 'November'; + + @override + String get december => 'December'; + + @override + String get time => 'Time'; + + @override + String get start => 'Start'; + + @override + String get end => 'End'; + + @override + String get notRated => 'Not rated'; + + @override + String get cascadeLabel => 'Select Item'; + + @override + String get back => 'BACK'; + + @override + String get top => 'TOP'; +} diff --git a/tdesign-component/example/lib/localizations/app_localizations_zh.dart b/tdesign-component/example/lib/localizations/app_localizations_zh.dart new file mode 100644 index 000000000..ec680e409 --- /dev/null +++ b/tdesign-component/example/lib/localizations/app_localizations_zh.dart @@ -0,0 +1,172 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Chinese (`zh`). +class AppLocalizationsZh extends AppLocalizations { + AppLocalizationsZh([String locale = 'zh']) : super(locale); + + @override + String get components => 'TDesign Flutter 组件库'; + + @override + String get about => '关于'; + + @override + String get defaultTheme => '默认主题'; + + @override + String get greenTheme => '绿色主题'; + + @override + String get redTheme => '红色主题'; + + @override + String get cancel => '取消'; + + @override + String get confirm => '确认'; + + @override + String get switchClose => '关'; + + @override + String get switchOpen => '开'; + + @override + String get knew => '知道了'; + + @override + String get loading => '加载中'; + + @override + String get loadingWithPoint => '加载中...'; + + @override + String get other => '其它'; + + @override + String get refreshing => '正在刷新'; + + @override + String get releaseRefresh => '松开刷新'; + + @override + String get pullToRefresh => '下拉刷新'; + + @override + String get completeRefresh => '刷新完成'; + + @override + String get reset => '重置'; + + @override + String get days => '天'; + + @override + String get hours => '时'; + + @override + String get minutes => '分'; + + @override + String get seconds => '秒'; + + @override + String get milliseconds => '毫秒'; + + @override + String get yearLabel => '年'; + + @override + String get monthLabel => '月'; + + @override + String get dateLabel => '日'; + + @override + String get weeks => '周'; + + @override + String get sunday => '日'; + + @override + String get monday => '一'; + + @override + String get tuesday => '二'; + + @override + String get wednesday => '三'; + + @override + String get thursday => '四'; + + @override + String get friday => '五'; + + @override + String get saturday => '六'; + + @override + String get year => ' 年'; + + @override + String get january => '1 月'; + + @override + String get february => '2 月'; + + @override + String get march => '3 月'; + + @override + String get april => '4 月'; + + @override + String get may => '5 月'; + + @override + String get june => '6 月'; + + @override + String get july => '7 月'; + + @override + String get august => '8 月'; + + @override + String get september => '9 月'; + + @override + String get october => '10 月'; + + @override + String get november => '11 月'; + + @override + String get december => '12 月'; + + @override + String get time => '时间'; + + @override + String get start => '开始'; + + @override + String get end => '结束'; + + @override + String get notRated => '未评分'; + + @override + String get cascadeLabel => '选择选项'; + + @override + String get back => '返回'; + + @override + String get top => '顶部'; +} diff --git a/tdesign-component/example/lib/main.dart b/tdesign-component/example/lib/main.dart new file mode 100644 index 000000000..3b3fad44f --- /dev/null +++ b/tdesign-component/example/lib/main.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:tdesign_flutter/src/util/log.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import 'base/example_route.dart'; +import 'base/intl_resource_delegate.dart'; +import 'config.dart'; +import 'home.dart'; +import 'localizations/app_localizations.dart'; + +void main() { + Log.setCustomLogPrinter((level, tag, msg) => print('[$level] $tag ==> $msg')); + runApp(const MyApp()); + + SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + systemNavigationBarColor: Colors.transparent, + systemNavigationBarDividerColor: Colors.transparent, + )); + + exampleMap.forEach((key, value) { + value.forEach((model) { + examplePageList.add(model); + }); + }); + sideBarExamplePage.forEach(examplePageList.add); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + late TDThemeData _themeData; + Locale? locale = const Locale('zh'); + + @override + void initState() { + super.initState(); + _themeData = TDThemeData.defaultData(); + } + + @override + Widget build(BuildContext context) { + // 使用多套主题 + TDTheme.needMultiTheme(); + // 适配3.16的字体居中前,先禁用字体居中功能 + // kTextForceVerticalCenterEnable = false; + var delegate = IntlResourceDelegate(context); + return MaterialApp( + title: 'TDesign Flutter Example', + theme: ThemeData(extensions: [_themeData], colorScheme: ColorScheme.light(primary: _themeData.brandNormalColor)), + home: PlatformUtil.isWeb ? null : Builder( + builder: (context) { + // 设置文案代理,国际化需要在MaterialApp初始化完成之后才生效,而且需要每次更新context + TDTheme.setResourceBuilder((context) => delegate..updateContext(context), needAlwaysBuild: true); + return MyHomePage( + title: AppLocalizations.of(context)?.components ?? '', + locale: locale, + onLocaleChange: (locale){ + setState(() { + this.locale = locale; + }); + }, + onThemeChange: (themeData) { + setState(() { + _themeData = themeData; + }); + }, + ); + }, + ), + // 设置国际化处理 + locale: locale, + supportedLocales: AppLocalizations.supportedLocales, + localizationsDelegates: AppLocalizations.localizationsDelegates, + onGenerateRoute: TDExampleRoute.onGenerateRoute, + routes: _getRoutes(), + ); + } + + Map _getRoutes() { + if (PlatformUtil.isWeb) { + return {for (var model in examplePageList) model.name: (context) => model.pageBuilder.call(context, model)} + ..putIfAbsent('/', () => (context) => const MyHomePage(title: 'TDesign Flutter 组件库')); + } else { + return const {}; + } + } +} diff --git a/tdesign-component/example/lib/page/sidebar/td_sidebar_page.dart b/tdesign-component/example/lib/page/sidebar/td_sidebar_page.dart new file mode 100644 index 000000000..669f0158a --- /dev/null +++ b/tdesign-component/example/lib/page/sidebar/td_sidebar_page.dart @@ -0,0 +1,136 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +/// +/// TDSideBarPage演示 +/// +class TDSideBarPage extends StatefulWidget { + const TDSideBarPage({Key? key}) : super(key: key); + + @override + State createState() { + return TDSideBarPageState(); + } +} + +class TDSideBarPageState extends State { + @override + Widget build(BuildContext context) { + var current = buildWidget(context); + return current; + } + + Widget buildWidget(BuildContext context) { + return ExamplePage( + title: tdTitle(), + exampleCodeGroup: 'sideBar', + desc: '用于内容分类后的展示切换。', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + desc: '侧边导航用法', + ignoreCode: true, + builder: _buildNavigatorSideBar), + ExampleItem( + desc: '图标侧边导航', + builder: _buildIconSideBar, + methodName: '_buildIconSideBar') + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem( + desc: '侧边导航样式', ignoreCode: true, builder: _buildStyleSideBar), + ]) + ], + test: [ + ExampleItem( + desc: '延迟加载', ignoreCode: true, builder: _loadingSideBar), + ], + ); + } + + Widget _buildNavigatorSideBar(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + CodeWrapper( + builder: (_) => getCustomButton(context, '锚点用法', 'SideBarAnchor'), + methodName: '_buildAnchorSideBar', + ), + const SizedBox( + height: 16, + ), + CodeWrapper( + builder: (_) => + getCustomButton(context, '切页用法', 'SideBarPagination'), + methodName: '_buildPaginationSideBar', + ), + ], + )); + } + + Widget _buildIconSideBar(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + getCustomButton(context, '带图标侧边导航', 'SideBarIcon'), + ], + )); + } + + Widget _buildStyleSideBar(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + CodeWrapper( + builder: (_) => + getCustomButton(context, '非通栏选项样式', 'SideBarOutline'), + methodName: '_buildOutlineSideBar', + ), + const SizedBox( + height: 16, + ), + CodeWrapper( + builder: (_) => + getCustomButton(context, '自定义样式', 'SideBarCustom'), + methodName: '_buildCustomSideBar', + ), + ], + )); + } + + Widget _loadingSideBar(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + CodeWrapper( + builder: (_) => + getCustomButton(context, '延迟加载', 'SideBarLoading'), + methodName: '_buildLoadingSideBar', + ), + ], + )); + } + + TDButton getCustomButton( + BuildContext context, String text, String routeName) { + return TDButton( + text: text, + width: MediaQuery.of(context).size.width - 16 * 2, + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + onTap: () { + Navigator.pushNamed(context, PlatformUtil.isWeb ? routeName : '$routeName'); + }, + ); + } +} diff --git a/tdesign-component/example/lib/page/sidebar/td_sidebar_page_anchor.dart b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_anchor.dart new file mode 100644 index 000000000..48cd8cfb5 --- /dev/null +++ b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_anchor.dart @@ -0,0 +1,208 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +/// +/// TDSideBarAnchorPage演示 +/// +class TDSideBarAnchorPage extends StatefulWidget { + const TDSideBarAnchorPage({Key? key}) : super(key: key); + + @override + State createState() { + return TDSideBarAnchorPageState(); + } +} + +class TDSideBarAnchorPageState extends State { + var currentValue = 1; + var itemHeight = 278.5; + final _demoScroller = ScrollController(initialScrollOffset: 278.5); + final _sideBarController = TDSideBarController(); + static const threshold = 50; + var lock = false; + + @override + void initState() { + super.initState(); + + _demoScroller.addListener(() { + if (lock) { + return; + } + + var scrollTop = _demoScroller.offset; + var index = (scrollTop + threshold) ~/ itemHeight; + + if (currentValue != index) { + setState(() { + _sideBarController.selectTo(index); + }); + } + }); + } + + Future onSelected(int value) async { + if (currentValue != value) { + setState(() { + currentValue = value; + }); + + lock = true; + await _demoScroller.animateTo(value.toDouble() * itemHeight, + duration: const Duration(milliseconds: 500), curve: Curves.easeIn); + lock = false; + } + } + + void onChanged(int value) { + if(mounted){ + setState(() { + currentValue = value; + }); + } + } + + @override + Widget build(BuildContext context) { + var current = buildWidget(context); + return current; + } + + Widget buildWidget(BuildContext context) { + return ExamplePage( + title: 'SideBar 锚点用法', + exampleCodeGroup: 'sideBar', + showSingleChild: true, + singleChild: CodeWrapper( + isCenter: false, + builder: _buildAnchorSideBar, + )); + } + + @Demo(group: 'sideBar') + Widget _buildAnchorSideBar(BuildContext context) { + // 锚点用法 + final list = []; + final pages = []; + + for (var i = 0; i < 20; i++) { + list.add(SideItemProps( + index: i, + label: '选项', + value: i, + )); + pages.add(getAnchorDemo(i)); + } + + pages.add(Container( + height: MediaQuery.of(context).size.height - itemHeight, + decoration: const BoxDecoration(color: Colors.white), + )); + + list[1].badge = const TDBadge(TDBadgeType.redPoint); + list[2].badge = const TDBadge( + TDBadgeType.message, + count: '8', + ); + + var demoHeight = MediaQuery.of(context).size.height; + _sideBarController.init(list); + + return Row( + children: [ + SizedBox( + width: 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.normal, + value: currentValue, + controller: _sideBarController, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + icon: ele.icon)) + .toList(), + onChanged: onChanged, + onSelected: onSelected, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: SingleChildScrollView( + controller: _demoScroller, + child: Column( + children: pages, + ), + ), + )) + ], + ); + } + + Widget getAnchorDemo(int index) { + return Container( + decoration: const BoxDecoration(color: Colors.white), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 20, top: 15, right: 9), + child: TDText('标题$index', + style: const TextStyle( + fontSize: 14, + )), + ), + Padding( + padding: const EdgeInsets.only(left: 20), + child: displayImageList(), + ), + ], + ), + ); + } + + Widget displayImageList() { + return Column( + children: [ + displayImageItem(), + const TDDivider(), + displayImageItem(), + const TDDivider(), + displayImageItem(), + const TDDivider(), + ], + ); + } + + Widget displayImageItem() { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: const [ + TDImage( + assetUrl: 'assets/img/empty.png', + type: TDImageType.roundedSquare, + width: 48, + height: 48, + ), + SizedBox( + width: 16, + ), + TDText( + '标题', + style: TextStyle( + fontSize: 16, + ), + ) + ], + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/sidebar/td_sidebar_page_custom.dart b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_custom.dart new file mode 100644 index 000000000..df01f0b8e --- /dev/null +++ b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_custom.dart @@ -0,0 +1,199 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +/// +/// TDSideBarCustomPage演示 +/// +class TDSideBarCustomPage extends StatefulWidget { + const TDSideBarCustomPage({Key? key}) : super(key: key); + + @override + State createState() { + return TDSideBarCustomPageState(); + } +} + +class TDSideBarCustomPageState extends State { + var currentValue = 1; + final _pageController = PageController(initialPage: 1); + final _sideBarController = TDSideBarController(); + + @override + Widget build(BuildContext context) { + var current = buildWidget(context); + return current; + } + + Widget buildWidget(BuildContext context) { + return ExamplePage( + title: 'SideBar 自定义样式', + exampleCodeGroup: 'sideBar', + showSingleChild: true, + singleChild: CodeWrapper( + isCenter: false, + builder: _buildCustomSideBar, + )); + } + + @Demo(group: 'sideBar') + Widget _buildCustomSideBar(BuildContext context) { + // 自定义样式 + final list = []; + final pages = []; + + for (var i = 0; i < 100; i++) { + list.add(SideItemProps( + index: i, + label: '选项', + value: i, + )); + pages.add(getPageDemo(i)); + } + + list[1].badge = const TDBadge(TDBadgeType.redPoint); + list[2].badge = const TDBadge( + TDBadgeType.message, + count: '8', + ); + list[1].textStyle = const TextStyle(color: Colors.green); + + void setCurrentValue(int value) { + _pageController.jumpToPage(value); + if (currentValue != value) { + currentValue = value; + } + } + + var demoHeight = MediaQuery.of(context).size.height; + + return Row( + children: [ + SizedBox( + width: 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.normal, + value: currentValue, + controller: _sideBarController, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + textStyle: ele.textStyle, + icon: ele.icon)) + .toList(), + selectedTextStyle:TextStyle(color: Colors.red), + onSelected: setCurrentValue, + contentPadding:EdgeInsets.only(left: 16, top: 16,bottom: 16), + selectedBgColor: Colors.blue, + unSelectedBgColor: Colors.yellow, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: PageView( + controller: _pageController, + scrollDirection: Axis.vertical, + children: pages, + physics: const NeverScrollableScrollPhysics(), + ), + )) + ], + ); + } + + Widget getPageDemo(int index) { + return Container( + decoration: const BoxDecoration(color: Colors.white), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 20, top: 2, right: 9), + child: TDText('标题$index', + style: const TextStyle( + fontSize: 14, + )), + ), + const SizedBox(height: 16), + displayImageList() + ], + ), + ); + } + + Widget getAnchorDemo(int index) { + return Container( + decoration: const BoxDecoration(color: Colors.white), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 20, top: 2, right: 9), + child: TDText('标题$index', + style: const TextStyle( + fontSize: 14, + )), + ), + const SizedBox(height: 16), + displayImageList() + ], + ), + ); + } + + Widget displayImageList() { + return Column( + children: [ + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + displayImageItem('标题文字'), + displayImageItem('标题文字'), + displayImageItem('最多六个文字'), + ]), + const SizedBox(height: 18), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + displayImageItem('标题文字'), + displayImageItem('标题文字'), + displayImageItem('最多六个文字'), + ], + ), + const SizedBox(height: 18), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + displayImageItem('标题文字'), + displayImageItem('标题文字'), + displayImageItem('最多六个文字'), + ], + ) + ], + ); + } + + Widget displayImageItem(String title) { + return Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const TDImage( + assetUrl: 'assets/img/empty.png', + type: TDImageType.roundedSquare, + width: 48, + height: 48, + ), + const SizedBox(height: 8), + TDText( + '$title', + style: const TextStyle(fontSize: 12), + ) + ], + )); + } +} diff --git a/tdesign-component/example/lib/page/sidebar/td_sidebar_page_icon.dart b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_icon.dart new file mode 100644 index 000000000..2e75979c0 --- /dev/null +++ b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_icon.dart @@ -0,0 +1,201 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +/// +/// TDSideBarIconPage演示 +/// +class TDSideBarIconPage extends StatefulWidget { + const TDSideBarIconPage({Key? key}) : super(key: key); + + @override + State createState() { + return TDSideBarIconPageState(); + } +} + +class TDSideBarIconPageState extends State { + var currentValue = 1; + var itemHeight = 278.5; + final _demoScroller = ScrollController(initialScrollOffset: 278.5); + final _sideBarController = TDSideBarController(); + static const threshold = 50; + var lock = false; + + @override + void initState() { + super.initState(); + + _demoScroller.addListener(() { + if (lock) { + return; + } + + var scrollTop = _demoScroller.offset; + var index = (scrollTop + threshold) ~/ itemHeight; + + if (currentValue != index) { + setState(() { + _sideBarController.selectTo(index); + }); + } + }); + } + + Future onSelected(int value) async { + if (currentValue != value) { + setState(() { + currentValue = value; + }); + + lock = true; + await _demoScroller.animateTo(value.toDouble() * itemHeight, + duration: const Duration(milliseconds: 500), curve: Curves.easeIn); + lock = false; + } + } + + void onChanged(int value) { + setState(() { + currentValue = value; + }); + } + + @override + Widget build(BuildContext context) { + var current = buildWidget(context); + return current; + } + + Widget buildWidget(BuildContext context) { + return ExamplePage( + title: 'SideBar 带图标侧边栏', + exampleCodeGroup: 'sideBar', + showSingleChild: true, + singleChild: CodeWrapper( + isCenter: false, + builder: _buildIconSideBar, + )); + } + + @Demo(group: 'sideBar') + Widget _buildIconSideBar(BuildContext context) { + final list = []; + final pages = []; + + for (var i = 0; i < 20; i++) { + list.add( + SideItemProps(index: i, label: '选项', value: i, icon: TDIcons.app)); + pages.add(getAnchorDemo(i)); + } + + pages.add(Container( + height: MediaQuery.of(context).size.height - itemHeight, + decoration: const BoxDecoration(color: Colors.white), + )); + + list[1].badge = const TDBadge(TDBadgeType.redPoint); + list[2].badge = const TDBadge( + TDBadgeType.message, + count: '8', + ); + + var demoHeight = MediaQuery.of(context).size.height; + + return Row( + children: [ + SizedBox( + width: 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.normal, + value: currentValue, + controller: _sideBarController, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + icon: ele.icon)) + .toList(), + onChanged: onChanged, + onSelected: onSelected, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: SingleChildScrollView( + controller: _demoScroller, + child: Column( + children: pages, + ), + ), + )) + ], + ); + } + + Widget getAnchorDemo(int index) { + return Container( + decoration: const BoxDecoration(color: Colors.white), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 20, top: 15, right: 9), + child: TDText('标题$index', + style: const TextStyle( + fontSize: 14, + )), + ), + Padding( + padding: const EdgeInsets.only(left: 20), + child: displayImageList(), + ), + ], + ), + ); + } + + Widget displayImageList() { + return Column( + children: [ + displayImageItem(), + const TDDivider(), + displayImageItem(), + const TDDivider(), + displayImageItem(), + const TDDivider(), + ], + ); + } + + Widget displayImageItem() { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: const [ + TDImage( + assetUrl: 'assets/img/empty.png', + type: TDImageType.roundedSquare, + width: 48, + height: 48, + ), + SizedBox( + width: 16, + ), + TDText( + '标题', + style: TextStyle( + fontSize: 16, + ), + ) + ], + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/sidebar/td_sidebar_page_loading.dart b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_loading.dart new file mode 100644 index 000000000..01dca141c --- /dev/null +++ b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_loading.dart @@ -0,0 +1,221 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +/// +/// TDSideBarLoadingPage演示 +/// +class TDSideBarLoadingPage extends StatefulWidget { + const TDSideBarLoadingPage({Key? key}) : super(key: key); + + @override + State createState() { + return TDSideBarLoadingPageState(); + } +} + +class TDSideBarLoadingPageState extends State { + var currentValue = 1; + var itemHeight = 278.5; + final _demoScroller = ScrollController(initialScrollOffset: 278.5); + final _sideBarController = TDSideBarController(); + static const threshold = 50; + var lock = false; + + @override + void initState() { + super.initState(); + + _demoScroller.addListener(() { + if (lock) { + return; + } + + var scrollTop = _demoScroller.offset; + var index = (scrollTop + threshold) ~/ itemHeight; + + if (currentValue != index) { + setState(() { + _sideBarController.selectTo(index); + }); + } + }); + } + + Future onSelected(int value) async { + if (currentValue != value) { + setState(() { + currentValue = value; + }); + + lock = true; + await _demoScroller.animateTo(value.toDouble() * itemHeight, + duration: const Duration(milliseconds: 500), curve: Curves.easeIn); + lock = false; + } + } + + void onChanged(int value) { + setState(() { + currentValue = value; + }); + } + + @override + Widget build(BuildContext context) { + var current = buildWidget(context); + return current; + } + + Widget buildWidget(BuildContext context) { + return ExamplePage( + title: 'SideBar 延迟加载', + exampleCodeGroup: 'sideBar', + showSingleChild: true, + singleChild: CodeWrapper( + isCenter: false, + builder: _buildLoadingSideBar, + )); + } + + List list = []; + List pages = []; + + void _initData() { + list = []; + pages = []; + for (var i = 0; i < 20; i++) { + list.add(SideItemProps( + index: i, + label: '选项', + value: i, + )); + pages.add(getLoadingDemo(i)); + } + + pages.add(Container( + height: MediaQuery.of(context).size.height - itemHeight, + decoration: const BoxDecoration(color: Colors.white), + )); + + list[1].badge = const TDBadge(TDBadgeType.redPoint); + list[2].badge = const TDBadge( + TDBadgeType.message, + count: '8', + ); + if(_sideBarController.loading) { + _sideBarController.init(list); + _sideBarController.selectTo(currentValue); + // 初始化时避免右侧内容与左侧item不匹配 + _demoScroller.animateTo(currentValue.toDouble() * itemHeight, + duration: const Duration(milliseconds: 1), curve: Curves.easeIn); + } + } + + @Demo(group: 'sideBar') + Widget _buildLoadingSideBar(BuildContext context) { + // 延迟加载 + Future.delayed(const Duration(seconds: 3), _initData); + var size = MediaQuery.of(context).size; + var demoHeight = size.height; + + return Row( + children: [ + SizedBox( + width: list.isEmpty ? size.width : 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.normal, + value: currentValue, + controller: _sideBarController, + loading: true, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + icon: ele.icon)) + .toList(), + onChanged: onChanged, + onSelected: onSelected, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: SingleChildScrollView( + controller: _demoScroller, + child: Column( + children: pages, + ), + ), + )) + ], + ); + } + + Widget getLoadingDemo(int index) { + return Container( + decoration: const BoxDecoration(color: Colors.white), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 20, top: 15, right: 9), + child: TDText('标题$index', + style: const TextStyle( + fontSize: 14, + )), + ), + Padding( + padding: const EdgeInsets.only(left: 20), + child: displayImageList(), + ), + ], + ), + ); + } + + Widget displayImageList() { + return Column( + children: [ + displayImageItem(), + const TDDivider(), + displayImageItem(), + const TDDivider(), + displayImageItem(), + const TDDivider(), + ], + ); + } + + Widget displayImageItem() { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: const [ + TDImage( + assetUrl: 'assets/img/empty.png', + type: TDImageType.roundedSquare, + width: 48, + height: 48, + ), + SizedBox( + width: 16, + ), + TDText( + '标题', + style: TextStyle( + fontSize: 16, + ), + ) + ], + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/sidebar/td_sidebar_page_outline.dart b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_outline.dart new file mode 100644 index 000000000..e86d05a35 --- /dev/null +++ b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_outline.dart @@ -0,0 +1,205 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +/// +/// TDSideBarOutlinePage演示 +/// +class TDSideBarOutlinePage extends StatefulWidget { + const TDSideBarOutlinePage({Key? key}) : super(key: key); + + @override + State createState() { + return TDSideBarOutlinePageState(); + } +} + +class TDSideBarOutlinePageState extends State { + var currentValue = 1; + var itemHeight = 278.5; + final _demoScroller = ScrollController(initialScrollOffset: 278.5); + final _sideBarController = TDSideBarController(); + static const threshold = 50; + var lock = false; + + @override + void initState() { + super.initState(); + + _demoScroller.addListener(() { + if (lock) { + return; + } + + var scrollTop = _demoScroller.offset; + var index = (scrollTop + threshold) ~/ itemHeight; + + if (currentValue != index) { + setState(() { + _sideBarController.selectTo(index); + }); + } + }); + } + + Future onSelected(int value) async { + if (currentValue != value) { + setState(() { + currentValue = value; + }); + + lock = true; + await _demoScroller.animateTo(value.toDouble() * itemHeight, + duration: const Duration(milliseconds: 500), curve: Curves.easeIn); + lock = false; + } + } + + void onChanged(int value) { + setState(() { + currentValue = value; + }); + } + + @override + Widget build(BuildContext context) { + var current = buildWidget(context); + return current; + } + + Widget buildWidget(BuildContext context) { + return ExamplePage( + title: 'SideBar 非通栏选项样式', + exampleCodeGroup: 'sideBar', + showSingleChild: true, + singleChild: CodeWrapper( + isCenter: false, + builder: _buildOutlineSideBar, + )); + } + + @Demo(group: 'sideBar') + Widget _buildOutlineSideBar(BuildContext context) { + // 非通栏选项样式 + final list = []; + final pages = []; + + for (var i = 0; i < 20; i++) { + list.add(SideItemProps( + index: i, + label: '选项', + value: i, + )); + pages.add(getAnchorDemo(i)); + } + + pages.add(Container( + height: MediaQuery.of(context).size.height - itemHeight, + decoration: const BoxDecoration(color: Colors.white), + )); + + list[1].badge = const TDBadge(TDBadgeType.redPoint); + list[2].badge = const TDBadge( + TDBadgeType.message, + count: '8', + ); + + var demoHeight = MediaQuery.of(context).size.height; + + return Row( + children: [ + SizedBox( + width: 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.outline, + value: currentValue, + controller: _sideBarController, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + icon: ele.icon)) + .toList(), + onChanged: onChanged, + onSelected: onSelected, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: SingleChildScrollView( + controller: _demoScroller, + child: Column( + children: pages, + ), + ), + )) + ], + ); + } + + Widget getAnchorDemo(int index) { + return Container( + decoration: const BoxDecoration(color: Colors.white), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 20, top: 15, right: 9), + child: TDText('标题$index', + style: const TextStyle( + fontSize: 14, + )), + ), + Padding( + padding: const EdgeInsets.only(left: 20), + child: displayImageList(), + ), + ], + ), + ); + } + + Widget displayImageList() { + return Column( + children: [ + displayImageItem(), + const TDDivider(), + displayImageItem(), + const TDDivider(), + displayImageItem(), + const TDDivider(), + ], + ); + } + + Widget displayImageItem() { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: const [ + TDImage( + assetUrl: 'assets/img/empty.png', + type: TDImageType.roundedSquare, + width: 48, + height: 48, + ), + SizedBox( + width: 16, + ), + TDText( + '标题', + style: TextStyle( + fontSize: 16, + ), + ) + ], + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/sidebar/td_sidebar_page_pagination.dart b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_pagination.dart new file mode 100644 index 000000000..970b4bb82 --- /dev/null +++ b/tdesign-component/example/lib/page/sidebar/td_sidebar_page_pagination.dart @@ -0,0 +1,193 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +/// +/// TDSideBarPaginationPage演示 +/// +class TDSideBarPaginationPage extends StatefulWidget { + const TDSideBarPaginationPage({Key? key}) : super(key: key); + + @override + State createState() { + return TDSideBarPaginationPageState(); + } +} + +class TDSideBarPaginationPageState extends State { + var currentValue = 1; + final _pageController = PageController(initialPage: 1); + final _sideBarController = TDSideBarController(); + + @override + Widget build(BuildContext context) { + var current = buildWidget(context); + return current; + } + + Widget buildWidget(BuildContext context) { + return ExamplePage( + title: 'SideBar 切页用法', + exampleCodeGroup: 'sideBar', + showSingleChild: true, + singleChild: CodeWrapper( + isCenter: false, + builder: _buildPaginationSideBar, + )); + } + + @Demo(group: 'sideBar') + Widget _buildPaginationSideBar(BuildContext context) { + // 切页用法 + final list = []; + final pages = []; + + for (var i = 0; i < 100; i++) { + list.add(SideItemProps( + index: i, + label: '选项', + value: i, + )); + pages.add(getPageDemo(i)); + } + + list[1].badge = const TDBadge(TDBadgeType.redPoint); + list[2].badge = const TDBadge( + TDBadgeType.message, + count: '8', + ); + + void setCurrentValue(int value) { + _pageController.jumpToPage(value); + if (currentValue != value) { + currentValue = value; + } + } + + var demoHeight = MediaQuery.of(context).size.height; + + return Row( + children: [ + SizedBox( + width: 110, + child: TDSideBar( + height: demoHeight, + style: TDSideBarStyle.normal, + value: currentValue, + controller: _sideBarController, + children: list + .map((ele) => TDSideBarItem( + label: ele.label ?? '', + badge: ele.badge, + value: ele.value, + icon: ele.icon)) + .toList(), + onSelected: setCurrentValue, + ), + ), + Expanded( + child: SizedBox( + height: demoHeight, + child: PageView( + controller: _pageController, + scrollDirection: Axis.vertical, + children: pages, + physics: const NeverScrollableScrollPhysics(), + ), + )) + ], + ); + } + + Widget getPageDemo(int index) { + return Container( + decoration: const BoxDecoration(color: Colors.white), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 20, top: 2, right: 9), + child: TDText('标题$index', + style: const TextStyle( + fontSize: 14, + )), + ), + const SizedBox(height: 16), + displayImageList() + ], + ), + ); + } + + Widget getAnchorDemo(int index) { + return Container( + decoration: const BoxDecoration(color: Colors.white), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(left: 20, top: 2, right: 9), + child: TDText('标题$index', + style: const TextStyle( + fontSize: 14, + )), + ), + const SizedBox(height: 16), + displayImageList() + ], + ), + ); + } + + Widget displayImageList() { + return Column( + children: [ + Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + displayImageItem('标题文字'), + displayImageItem('标题文字'), + displayImageItem('最多六个文字'), + ]), + const SizedBox(height: 18), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + displayImageItem('标题文字'), + displayImageItem('标题文字'), + displayImageItem('最多六个文字'), + ], + ), + const SizedBox(height: 18), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + displayImageItem('标题文字'), + displayImageItem('标题文字'), + displayImageItem('最多六个文字'), + ], + ) + ], + ); + } + + Widget displayImageItem(String title) { + return Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const TDImage( + assetUrl: 'assets/img/empty.png', + type: TDImageType.roundedSquare, + width: 48, + height: 48, + ), + const SizedBox(height: 8), + TDText( + '$title', + style: const TextStyle(fontSize: 12), + ) + ], + )); + } +} diff --git a/tdesign-component/example/lib/page/td_action_sheet_page.dart b/tdesign-component/example/lib/page/td_action_sheet_page.dart new file mode 100644 index 000000000..a4fcc5116 --- /dev/null +++ b/tdesign-component/example/lib/page/td_action_sheet_page.dart @@ -0,0 +1,613 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class IconWithBackground extends StatelessWidget { + final IconData icon; + + const IconWithBackground({ + Key? key, + required this.icon, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + width: 40.0, + height: 40.0, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault), + ), + child: Center( + child: Icon( + icon, + size: 24.0, + ), + ), + ); + } +} + +const _nums = ['一', '二', '三', '四']; +List _gridItems = [ + TDActionSheetItem(label: '微信', icon: Image.asset('assets/img/td_action_sheet_1.png'), group: '分享至'), + TDActionSheetItem(label: '朋友圈', icon: Image.asset('assets/img/td_action_sheet_2.png'), group: '分享至'), + TDActionSheetItem(label: 'QQ', icon: Image.asset('assets/img/td_action_sheet_3.png'), group: '分享至'), + TDActionSheetItem(label: '企业微信', icon: Image.asset('assets/img/td_action_sheet_4.png'), group: '分享至'), + TDActionSheetItem(label: '收藏', icon: const IconWithBackground(icon: TDIcons.star), group: '分享至'), + TDActionSheetItem(label: '刷新', icon: const IconWithBackground(icon: TDIcons.refresh), group: '分享至'), + TDActionSheetItem(label: '下载', icon: const IconWithBackground(icon: TDIcons.download), group: '分享至'), + TDActionSheetItem(label: '复制', icon: const IconWithBackground(icon: TDIcons.queue), group: '分享至'), +]; + +class TDActionSheetPage extends StatelessWidget { + const TDActionSheetPage({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: TDTheme.of(context).grayColor2, + child: ExamplePage( + title: tdTitle(context), + desc: '从底部弹出的模态框,提供和当前场景相关的操作动作,也支持提供信息输入和描述。', + exampleCodeGroup: 'action_sheet', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + ignoreCode: true, + desc: '列表型动作面板', + builder: (BuildContext context) { + return const Column( + children: [ + CodeWrapper(builder: _buildBaseListActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildDescListActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildIconListActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildBadgeListActionSheet), + ], + ); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '宫格型动作面板', + builder: (BuildContext context) { + return const Column( + children: [ + CodeWrapper(builder: _buildBaseGridActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildDescGridActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildPaginationGridActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildScrollGridActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildMultiScrollGridActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildBadgeGridActionSheet), + ], + ); + }, + ), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem( + ignoreCode: true, + desc: '列表型选项状态', + builder: (BuildContext context) { + return const Column( + children: [ + CodeWrapper(builder: _buildBaseListStateActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildIconListStateActionSheet), + ], + ); + }, + ) + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem( + ignoreCode: true, + desc: '列表型对齐方式', + builder: (BuildContext context) { + return const Column( + children: [ + CodeWrapper(builder: _buildBadgeListCenterActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildIconListCenterActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildBadgeListLeftActionSheet), + SizedBox(height: 16), + CodeWrapper(builder: _buildIconListLeftActionSheet), + ], + ); + }) + ]) + ], + test: const [], + )); + } +} + +@Demo(group: 'action_sheet') +Widget _buildBaseListActionSheet(BuildContext context) { + return TDButton( + text: '常规列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + items: _nums.map((e) => TDActionSheetItem(label: '选项$e')).toList(), + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildDescListActionSheet(BuildContext context) { + return TDButton( + text: '带描述列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + description: '动作面板描述文字', + items: _nums.map((e) => TDActionSheetItem(label: '选项$e')).toList(), + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildIconListActionSheet(BuildContext context) { + return TDButton( + text: '带图标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + items: _nums + .map((e) => TDActionSheetItem( + label: '选项$e', + icon: const Icon(TDIcons.app), + )) + .toList(), + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildBadgeListActionSheet(BuildContext context) { + return TDButton( + text: '带徽标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + items: [ + TDActionSheetItem( + label: '选项一', + badge: const TDBadge(TDBadgeType.redPoint), + ), + TDActionSheetItem( + label: '选项二', + badge: const TDBadge(TDBadgeType.message, count: '8'), + ), + TDActionSheetItem( + label: '选项三', + badge: const TDBadge(TDBadgeType.message, count: '99'), + ), + TDActionSheetItem( + label: '选项四', + badge: const TDBadge(TDBadgeType.message, count: '99+'), + ), + ], + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildBaseGridActionSheet(BuildContext context) { + return TDButton( + text: '常规宫格', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + theme: TDActionSheetTheme.grid, + count: 8, + items: _gridItems, + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildDescGridActionSheet(BuildContext context) { + return TDButton( + text: '带描述宫格', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + theme: TDActionSheetTheme.grid, + count: 8, + description: '动作面板描述文字', + items: _gridItems, + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildPaginationGridActionSheet(BuildContext context) { + return TDButton( + text: '带翻页宫格', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + theme: TDActionSheetTheme.grid, + count: 8, + showPagination: true, + items: [ + ..._gridItems, + TDActionSheetItem( + label: '安卓', + icon: const IconWithBackground(icon: TDIcons.logo_android), + ), + TDActionSheetItem( + label: 'Apple', + icon: const IconWithBackground(icon: TDIcons.logo_apple), + ), + TDActionSheetItem( + label: 'Chrome', + icon: const IconWithBackground(icon: TDIcons.logo_chrome), + ), + TDActionSheetItem( + label: 'Github', + icon: const IconWithBackground(icon: TDIcons.logo_github), + ), + ], + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildScrollGridActionSheet(BuildContext context) { + return TDButton( + text: '多行滚动宫格', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + theme: TDActionSheetTheme.grid, + count: 8, + scrollable: true, + items: [ + ..._gridItems, + TDActionSheetItem( + label: '安卓', + icon: const IconWithBackground(icon: TDIcons.logo_android), + ), + TDActionSheetItem( + label: 'Apple', + icon: const IconWithBackground(icon: TDIcons.logo_apple), + ), + TDActionSheetItem( + label: 'Chrome', + icon: const IconWithBackground(icon: TDIcons.logo_chrome), + ), + TDActionSheetItem( + label: 'Github', + icon: const IconWithBackground(icon: TDIcons.logo_github), + ), + TDActionSheetItem( + label: 'Github', + icon: const IconWithBackground(icon: TDIcons.logo_github), + ), + ], + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildMultiScrollGridActionSheet(BuildContext context) { + return TDButton( + text: '带描述多行滚动宫格', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet.showGroupActionSheet(context, items: [ + TDActionSheetItem( + label: 'Allen', + icon: Image.asset('assets/img/td_action_sheet_5.png'), + group: '分享给好友', + ), + TDActionSheetItem( + label: 'Nick', + icon: Image.asset('assets/img/td_action_sheet_6.png'), + group: '分享给好友', + ), + TDActionSheetItem( + label: 'Jacky', + icon: Image.asset('assets/img/td_action_sheet_7.png'), + group: '分享给好友', + ), + TDActionSheetItem( + label: 'Eric', + icon: Image.asset('assets/img/td_action_sheet_8.png'), + group: '分享给好友', + ), + TDActionSheetItem( + label: 'Johnsc', + icon: Image.asset('assets/img/td_action_sheet_5.png'), + group: '分享给好友', + ), + TDActionSheetItem( + label: 'Kevin', + icon: Image.asset('assets/img/td_action_sheet_6.png'), + group: '分享给好友', + ), + ..._gridItems, + ]); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildBadgeGridActionSheet(BuildContext context) { + return TDButton( + text: '带徽标宫格型', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet.showGridActionSheet(context, items: [ + TDActionSheetItem(label: '微信', icon: Image.asset('assets/img/td_action_sheet_1.png'), badge: const TDBadge(TDBadgeType.message, count: 'NEW')), + TDActionSheetItem(label: '朋友圈', icon: Image.asset('assets/img/td_action_sheet_2.png')), + TDActionSheetItem(label: 'QQ', icon: Image.asset('assets/img/td_action_sheet_3.png')), + TDActionSheetItem(label: '企业微信', icon: Image.asset('assets/img/td_action_sheet_4.png')), + TDActionSheetItem(label: '收藏', icon: const IconWithBackground(icon: TDIcons.star), badge: const TDBadge(TDBadgeType.redPoint)), + TDActionSheetItem(label: '刷新', icon: const IconWithBackground(icon: TDIcons.refresh)), + TDActionSheetItem(label: '下载', icon: const IconWithBackground(icon: TDIcons.download), badge: const TDBadge(TDBadgeType.message, count: '8')), + TDActionSheetItem(label: '复制', icon: const IconWithBackground(icon: TDIcons.queue)), + ]); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildBaseListStateActionSheet(BuildContext context) { + return TDButton( + text: '列表型选项状态', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + items: [ + TDActionSheetItem( + label: '默认选项', + ), + TDActionSheetItem( + label: '自定义选项', + textStyle: TextStyle( + color: TDTheme.of(context).brandNormalColor, + ), + ), + TDActionSheetItem( + label: '失效选项', + disabled: true, + ), + TDActionSheetItem( + label: '警告选项', + textStyle: const TextStyle( + color: Colors.red, + ), + ), + ], + onSelected: (item, index) { + print('选中了:${item.label}'); + }, + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildIconListStateActionSheet(BuildContext context) { + return TDButton( + text: '列表型带图标状态', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + items: [ + TDActionSheetItem( + label: '默认选项', + icon: const Icon(TDIcons.app), + ), + TDActionSheetItem( + label: '自定义选项', + icon: const Icon(TDIcons.app), + textStyle: TextStyle( + color: TDTheme.of(context).brandNormalColor, + ), + ), + TDActionSheetItem( + label: '失效选项', + icon: const Icon(TDIcons.app), + disabled: true, + ), + TDActionSheetItem( + label: '警告选项', + icon: const Icon(TDIcons.app), + textStyle: const TextStyle( + color: Colors.red, + ), + ), + ], + onSelected: (item, index) { + print('选中了:${item.label}'); + }, + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildBadgeListCenterActionSheet(BuildContext context) { + return TDButton( + text: '居中带徽标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + description: '动作面板描述文字', + items: [ + TDActionSheetItem( + label: '选项一', + badge: const TDBadge(TDBadgeType.redPoint), + ), + TDActionSheetItem( + label: '选项二', + badge: const TDBadge(TDBadgeType.message, count: '8',), + ), + TDActionSheetItem( + label: '选项三', + badge: const TDBadge(TDBadgeType.message, count: '99',), + ), + ], + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildIconListCenterActionSheet(BuildContext context) { + return TDButton( + text: '居中带图标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + description: '动作面板描述文字', + items: _nums + .map((e) => TDActionSheetItem( + label: '选项$e', + icon: const Icon(TDIcons.app), + )) + .toList(), + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildBadgeListLeftActionSheet(BuildContext context) { + return TDButton( + text: '左对齐带徽标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + description: '动作面板描述文字', + align: TDActionSheetAlign.left, + items: _nums + .map((e) => TDActionSheetItem( + label: '选项$e', + badge: const TDBadge(TDBadgeType.redPoint), + )) + .toList(), + ); + }, + ); +} + +@Demo(group: 'action_sheet') +Widget _buildIconListLeftActionSheet(BuildContext context) { + return TDButton( + text: '左对齐带图标列表', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDActionSheet( + context, + visible: true, + description: '动作面板描述文字', + align: TDActionSheetAlign.left, + items: _nums + .map((e) => TDActionSheetItem( + label: '选项$e', + icon: const Icon(TDIcons.app), + )) + .toList(), + ); + }, + ); +} diff --git a/tdesign-component/example/lib/page/td_avatar_page.dart b/tdesign-component/example/lib/page/td_avatar_page.dart new file mode 100644 index 000000000..003b41340 --- /dev/null +++ b/tdesign-component/example/lib/page/td_avatar_page.dart @@ -0,0 +1,290 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDAvatarPage extends StatefulWidget { + const TDAvatarPage({Key? key}) : super(key: key); + + @override + State createState() => _TDAvatarPageState(); +} + +class _TDAvatarPageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + backgroundColor: TDTheme.of(context).whiteColor1, + title: tdTitle(), + exampleCodeGroup: 'avatar', + desc: '用于告知用户,该区域的状态变化或者待处理任务的数量。', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + desc: '图片头像', + builder: _buildImageAvatar), + ExampleItem( + desc: '字符头像', + builder: _buildTextAvatar), + ExampleItem( + desc: '图标头像', + builder: _buildIconAvatar), + ExampleItem( + desc: '带徽标头像', + builder: _buildBadgeAvatar), + ]), + ExampleModule(title: '特殊类型', children: [ + ExampleItem( + desc: '纯展示的头像组', + builder: _buildDisplayAvatar), + ExampleItem( + desc: '带操作的头像组', + builder: _buildOperationAvatar), + ]), + ExampleModule(title: '组件尺寸', children: [ + ExampleItem( + desc: '大尺寸 :64px', + builder: _buildLargeAvatar), + ExampleItem( + desc: '中尺寸 :48px', + builder: _buildMediumAvatar), + ExampleItem( + desc: '小尺寸 :40px', + builder: _buildSmallAvatar), + ]) + ], + ); + } + + /// 图片头像 + @Demo(group: 'avatar') + Widget _buildImageAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.normal, + defaultUrl: 'assets/img/td_avatar_1.png',), + SizedBox(width: 32,), + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.normal, + shape: TDAvatarShape.square, + defaultUrl: 'assets/img/td_avatar_1.png',), + ], + ), + ); + } + + /// 字符头像 + @Demo(group: 'avatar') + Widget _buildTextAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.customText, + text: 'A'), + SizedBox(width: 32,), + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.customText, + shape: TDAvatarShape.square, + text: 'A'), + ], + ), + ); + } + + /// 图标头像 + @Demo(group: 'avatar') + Widget _buildIconAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.icon,), + SizedBox(width: 32,), + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.icon, + shape: TDAvatarShape.square, + ), + ], + ), + ); + } + + /// 带徽标头像 + @Demo(group: 'avatar') + Widget _buildBadgeAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + height: 51, + width: 51, + child: Stack( + alignment:Alignment.bottomLeft, + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.normal, + defaultUrl: 'assets/img/td_avatar_1.png',), + Positioned(child: TDBadge(TDBadgeType.redPoint), right: 0, top: 0) + ], + ), + ), + const SizedBox(width: 32,), + SizedBox( + height: 51, + width: 51, + child: Stack( + alignment:Alignment.bottomLeft, + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.customText, + text: 'A'), + Positioned(child: TDBadge(TDBadgeType.message,count: '8',), right: 0, top: 0) + ], + ), + ), + const SizedBox(width: 32,), + SizedBox( + width: 51, + height: 51, + child: Stack( + alignment:Alignment.bottomLeft, + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.icon,), + Positioned(child: TDBadge(TDBadgeType.message,count: '12',), right: 0, top: 0,) + ], + ), + ), + ], + ), + ); + } + + /// 纯展示的头像组 + @Demo(group: 'avatar') + Widget _buildDisplayAvatar(BuildContext context){ + var assetUrl = 'assets/img/td_avatar_1.png'; + var assetUrl2 = 'assets/img/td_avatar_2.png'; + var avatarList = [assetUrl, assetUrl2, assetUrl, assetUrl2, assetUrl]; + return Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.only(left: 16), + child: TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.display, + displayText: '+5', + avatarDisplayListAsset: avatarList,), + ); + } + + /// 带操作的头像组 + @Demo(group: 'avatar') + Widget _buildOperationAvatar(BuildContext context){ + var assetUrl = 'assets/img/td_avatar_1.png'; + var assetUrl2 = 'assets/img/td_avatar_2.png'; + var avatarList = [assetUrl, assetUrl2, assetUrl, assetUrl2, assetUrl]; + return Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.only(left: 16), + child: TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.operation, + avatarDisplayListAsset: avatarList, + onTap: () { + TDToast.showText('点击了操作', context: context); + }), + ); + } + + /// 组件尺寸 大尺寸 + @Demo(group: 'avatar') + Widget _buildLargeAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.large, + type: TDAvatarType.normal, + defaultUrl: 'assets/img/td_avatar_1.png',), + SizedBox(width: 32,), + TDAvatar( + size: TDAvatarSize.large, + type: TDAvatarType.customText, + text: 'A'), + SizedBox(width: 32,), + TDAvatar( + size: TDAvatarSize.large, + type: TDAvatarType.icon,), + ], + ), + ); + } + + /// 组件尺寸 中尺寸 + @Demo(group: 'avatar') + Widget _buildMediumAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.normal, + defaultUrl: 'assets/img/td_avatar_1.png',), + SizedBox(width: 48,), + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.customText, + text: 'A'), + SizedBox(width: 48,), + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.icon,), + ], + ), + ); + } + + /// 组件尺寸 小尺寸 + @Demo(group: 'avatar') + Widget _buildSmallAvatar(BuildContext context){ + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: const [ + TDAvatar( + size: TDAvatarSize.small, + type: TDAvatarType.normal, + defaultUrl: 'assets/img/td_avatar_1.png',), + SizedBox(width: 56,), + TDAvatar( + size: TDAvatarSize.small, + type: TDAvatarType.customText, + text: 'A'), + SizedBox(width: 56,), + TDAvatar( + size: TDAvatarSize.small, + type: TDAvatarType.icon,), + ], + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_backtop_page.dart b/tdesign-component/example/lib/page/td_backtop_page.dart new file mode 100644 index 000000000..cbad63642 --- /dev/null +++ b/tdesign-component/example/lib/page/td_backtop_page.dart @@ -0,0 +1,176 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + + +class TDBackTopPage extends StatefulWidget { + const TDBackTopPage({Key? key}) : super(key: key); + + @override + State createState() => _TDBackTopPageState(); +} + +class _TDBackTopPageState extends State { + ScrollController controller = ScrollController(); + bool showBackTop = false; + TDBackTopStyle style = TDBackTopStyle.circle; + + @override + void initState() { + super.initState(); + controller.addListener(listenCallback); + } + + @override + void dispose() { + super.dispose(); + controller.removeListener(listenCallback); + } + + void listenCallback() { + if (controller.offset >= 100) { + if (!showBackTop) { + setState(() { + showBackTop = true; + }); + } + } else { + if (showBackTop) { + setState(() { + showBackTop = false; + }); + } + } + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + scrollController: controller, + title: tdTitle(), + desc: '用于当页面过长往下滑动时,帮助用户快速回到页面顶部。', + exampleCodeGroup: 'backtop', + floatingActionButton: Stack( + clipBehavior: Clip.none, + children: [ + Visibility( + visible: showBackTop, + child: style == TDBackTopStyle.halfCircle + ? Positioned( + right: -16, + bottom: 10, + child: TDBackTop( + controller: controller, + theme: TDBackTopTheme.dark, + showText: true, + style: style, + )) + : TDBackTop( + controller: controller, + theme: TDBackTopTheme.dark, + showText: true, + style: style, + )) + ], + ), + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '圆形返回顶部', builder: _buildCircleBackTop), + ExampleItem(desc: '半圆形返回顶部', builder: _buildHalfCircleBackTop), + ]) + ]); + } + + @Demo(group: 'backtop') + Widget _buildCircleBackTop(BuildContext context) { + return getCustomButton(context, '圆形返回顶部', () { + setState(() { + showBackTop = true; + if (controller.hasClients) { + controller.jumpTo(500); + } + style = TDBackTopStyle.circle; + }); + }); + } + + @Demo(group: 'backtop') + Widget _buildHalfCircleBackTop(BuildContext context) { + return Column( + children: [ + getCustomButton(context, '半圆形返回顶部', () { + setState(() { + showBackTop = true; + if (controller.hasClients) { + controller.jumpTo(500); + } + style = TDBackTopStyle.halfCircle; + }); + }), + Padding( + padding: const EdgeInsets.only(left: 16, right: 16, top: 24), + child: Wrap( + spacing: 16, + runSpacing: 24, + children: [ + getDemoBox(context), + getDemoBox(context), + getDemoBox(context), + getDemoBox(context), + getDemoBox(context), + getDemoBox(context), + ], + ), + ) + ], + ); + } + + Widget getCustomButton( + BuildContext context, String text, void Function() onTap) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: TDButton( + text: text, + width: MediaQuery.of(context).size.width - 16 * 2, + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + onTap: onTap, + )); + } + + Widget getDemoBox(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 163, + height: 163, + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(12)), + ), + const SizedBox( + height: 10, + ), + Container( + width: 163, + height: 16, + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(3)), + ), + const SizedBox( + height: 10, + ), + Container( + width: 100, + height: 16, + decoration: BoxDecoration( + color: Colors.white, borderRadius: BorderRadius.circular(3))), + ], + ); + } +} diff --git a/tdesign-component/example/lib/page/td_badge_page.dart b/tdesign-component/example/lib/page/td_badge_page.dart new file mode 100644 index 000000000..d81df2424 --- /dev/null +++ b/tdesign-component/example/lib/page/td_badge_page.dart @@ -0,0 +1,637 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDBadgePage extends StatefulWidget { + const TDBadgePage({Key? key}) : super(key: key); + + @override + State createState() => _TDBadgePageState(); +} + +class _TDBadgePageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '用于告知用户,该区域的状态变化或者待处理任务的数量。', + exampleCodeGroup: 'badge', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem( + desc: '红点徽标', + ignoreCode: true, + builder: (context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CodeWrapper( + builder: _buildRedPointMessageBadge, + ), + CodeWrapper( + builder: _buildRedPointIconBadge, + ), + CodeWrapper( + builder: _buildRedPointButtonBadge, + ), + ], + ); + }), + + ExampleItem( + desc: '数字徽标', + ignoreCode: true, + builder: (context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CodeWrapper( + builder: _buildMessageNumberBadge, + ), + CodeWrapper( + builder: _buildIconNumberBadge, + ), + CodeWrapper( + builder: _buildButtonNumberBadge, + ), + ], + ); + }), + ExampleItem( + desc: '自定义徽标', + ignoreCode: true, + builder: (context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CodeWrapper( + builder: _buildCustomBadgeShowingNumberEight, + ), + CodeWrapper( + builder: _buildCustomBadgeShowingNumberZero, + ), + CodeWrapper( + builder: _buildCustomBadgeWithoutShowingNumberZero, + ), + ], + ); + }), + ], + ), + ExampleModule( + title: '组件样式', + children: [ + ExampleItem(desc: '圆形徽标', builder: _buildCircleBadge), + ExampleItem(desc: '方形徽标', builder: _buildSquareBadge), + ExampleItem(desc: '气泡徽标', builder: _buildBubbleBadge), + ExampleItem(desc: '角标', builder: _buildSubscriptBadge), + ], + ), + ExampleModule( + title: '组件尺寸', + children: [ + ExampleItem(desc: 'Large', builder: _buildLargeBadge), + ExampleItem(desc: 'Medium', builder: _buildMediumBadge) + ], + ), + ], + test: [ + ExampleItem( + ignoreCode: true, + desc: '未超过上限', + builder: (context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CodeWrapper( + builder: _buildLessThanMaxCountBadge, + ), + ], + ); + }), + ExampleItem( + ignoreCode: true, + desc: '超过上限', + builder: (context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + CodeWrapper( + builder: _buildMoreThanMaxCountBadge, + ), + ], + ); + }) + ], + ); + } + + @Demo(group: 'badge') + Widget _buildRedPointMessageBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16, right: 16), + child: SizedBox( + width: 40, + height: 24, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDText( + '消息', + font: TDTheme.of(context).fontBodyLarge, + ), + const Positioned( + child: TDBadge(TDBadgeType.redPoint), + right: 0, + top: 0, + ) + ], + ), + )); + } + + @Demo(group: 'badge') + Widget _buildRedPointIconBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16, right: 16), + child: SizedBox( + width: 27, + height: 27, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Icon(TDIcons.notification), + Positioned( + child: TDBadge(TDBadgeType.redPoint), + right: 0, + top: 0, + ) + ], + ), + )); + } + + @Demo(group: 'badge') + Widget _buildRedPointButtonBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16, right: 16), + child: SizedBox( + width: 83, + height: 48, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDButton( + width: 80, + height: 48, + text: '按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + ), + Positioned( + child: TDBadge(TDBadgeType.redPoint), + right: 0, + top: 0, + ) + ], + ), + )); + } + + @Demo(group: 'badge') + Widget _buildMessageNumberBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16), + child: SizedBox( + width: 54, + height: 36, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDText( + '消息', + font: TDTheme.of(context).fontBodyLarge, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + left: 28, + bottom: 18, + ) + ], + ), + )); + } + + @Demo(group: 'badge') + Widget _buildIconNumberBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16), + child: SizedBox( + width: 42, + height: 36, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Icon(TDIcons.notification), + Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + left: 18, + bottom: 18, + ) + ], + ), + )); + } + + @Demo(group: 'badge') + Widget _buildButtonNumberBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16), + child: SizedBox( + width: 86, + height: 54, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDButton( + width: 80, + height: 48, + text: '按钮', + size: TDButtonSize.large, + ), + Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + right: 0, + top: 0, + ) + ], + ), + )); + } + + @Demo(group: 'badge') + Widget _buildCustomBadgeShowingNumberEight(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(top: 8, left: 16, right: 16, bottom: 8), + child: SizedBox( + width: 64, + height: 56, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + child: const Icon(TDIcons.notification), + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.circular( + TDTheme.of(context).radiusDefault)), + height: 48, + width: 48, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + right: 0, + top: 0, + ) + ], + ), + )); + } + + @Demo(group: 'badge') + Widget _buildCustomBadgeShowingNumberZero(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(top: 8, left: 16, right: 16, bottom: 8), + child: SizedBox( + width: 64, + height: 56, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + child: const Icon(TDIcons.notification), + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.circular( + TDTheme.of(context).radiusDefault)), + height: 48, + width: 48, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '0', + ), + right: 0, + top: 0, + ) + ], + ), + )); + } + + @Demo(group: 'badge') + Widget _buildCustomBadgeWithoutShowingNumberZero(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(top: 8, left: 16, right: 16, bottom: 8), + child: SizedBox( + width: 64, + height: 56, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + child: const Icon(TDIcons.notification), + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.circular( + TDTheme.of(context).radiusDefault)), + height: 48, + width: 48, + ), + const Positioned( + child: TDBadge( + TDBadgeType.message, + count: '0', + showZero: false, + ), + right: 0, + top: 0, + ) + ], + ), + )); + } + + @Demo(group: 'badge') + Widget _buildCircleBadge(BuildContext context) { + return Padding( + padding: EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 48, + height: 34, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Icon(TDIcons.notification), + Positioned( + child: TDBadge( + TDBadgeType.message, + count: '16', + ), + left: 18, + bottom: 18, + ) + ], + ), + ), + ], + ), + ); + } + + @Demo(group: 'badge') + Widget _buildSquareBadge(BuildContext context) { + return Padding( + padding: EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 48, + height: 34, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Icon(TDIcons.notification), + Positioned( + child: TDBadge( + TDBadgeType.square, + border: TDBadgeBorder.small, + count: '16', + ), + left: 20, + bottom: 18, + ) + ], + ), + ), + ], + ), + ); + } + + @Demo(group: 'badge') + Widget _buildBubbleBadge(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 67, + height: 56, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + Container( + child: const Icon(TDIcons.shop), + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.circular( + TDTheme.of(context).radiusDefault)), + height: 48, + width: 48, + ), + const Positioned( + child: TDBadge( + TDBadgeType.bubble, + count: '领积分', + ), + right: 0, + top: 0, + ) + ], + ), + ), + ], + ), + ); + } + + @Demo(group: 'badge') + Widget _buildSubscriptBadge(BuildContext context) { + return Stack( + alignment: Alignment.topRight, + children: [ + Container( + padding: const EdgeInsets.only(left: 16), + alignment: Alignment.centerLeft, + child: TDText( + '单行标题', + textColor: TDTheme.of(context).fontGyColor1, + font: TDTheme.of(context).fontBodyLarge, + ), + color: Colors.white, + height: 48, + width: MediaQuery.of(context).size.width, + ), + const TDBadge( + TDBadgeType.subscript, + message: 'NEW', + ), + ], + ); + } + + @Demo(group: 'badge') + Widget _buildLargeBadge(BuildContext context) { + return Padding( + padding: EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 68, + height: 70, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDAvatar( + size: TDAvatarSize.large, + type: TDAvatarType.icon, + ), + Positioned( + child: TDBadge( + TDBadgeType.message, + size: TDBadgeSize.large, + count: '8', + ), + left: 48, + bottom: 48, + ) + ], + ), + ), + ], + ), + ); + } + + @Demo(group: 'badge') + Widget _buildMediumBadge(BuildContext context) { + return Container( + padding: const EdgeInsets.only(left: 16), + child: Row( + children: [ + SizedBox( + width: 120, + height: 54, + child: Stack( + alignment: Alignment.bottomLeft, + children: [ + TDAvatar( + size: TDAvatarSize.medium, + type: TDAvatarType.icon, + ), + Positioned( + child: TDBadge( + TDBadgeType.message, + count: '8', + ), + left: 36, + bottom: 36, + ) + ], + ), + ), + ], + ), + ); + } + + @Demo(group: 'badge') + Widget _buildLessThanMaxCountBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16, right: 16, bottom: 8), + child: SizedBox( + width: 60, + height: 50, + child: Stack( + children: [ + Positioned( + left: 0, + bottom: 0, + child: Icon(TDIcons.notification), + ), + Positioned( + child: TDBadge( + TDBadgeType.square, + count: '8888', + maxCount: '9000', + size: TDBadgeSize.large, + border: TDBadgeBorder.large, + ), + left: 18, + bottom: 18, + ), + ], + ), + )); + } + + @Demo(group: 'badge') + Widget _buildMoreThanMaxCountBadge(BuildContext context) { + return Container( + alignment: Alignment.bottomLeft, + margin: const EdgeInsets.only(left: 16, right: 16, bottom: 8), + child: SizedBox( + width: 60, + height: 50, + child: Stack( + children: [ + Positioned( + left: 0, + bottom: 0, + child: Icon(TDIcons.notification), + ), + Positioned( + child: TDBadge( + TDBadgeType.square, + count: '888', + maxCount: '99', + size: TDBadgeSize.large, + border: TDBadgeBorder.large, + ), + left: 18, + bottom: 18, + ), + ], + ), + )); + } +} diff --git a/tdesign-component/example/lib/page/td_bottom_tab_bar_page.dart b/tdesign-component/example/lib/page/td_bottom_tab_bar_page.dart new file mode 100644 index 000000000..6b0191964 --- /dev/null +++ b/tdesign-component/example/lib/page/td_bottom_tab_bar_page.dart @@ -0,0 +1,1117 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +Widget? _selectedIcon; + +Widget? _unSelectedIcon; + +class TDBottomTabBarPage extends StatefulWidget { + const TDBottomTabBarPage({Key? key}) : super(key: key); + + @override + State createState() => _TDBottomTabBarPageState(); +} + +class _TDBottomTabBarPageState extends State { + void onTapTab( + BuildContext context, + String tabName, + ) { + TDToast.showText('点击了 $tabName', context: context); + } + + @override + Widget build(BuildContext context) { + _selectedIcon = Icon( + TDIcons.app, + size: 20, + color: TDTheme.of(context).brandNormalColor, + ); + _unSelectedIcon = Icon( + TDIcons.app, + size: 20, + color: TDTheme.of(context).brandNormalColor, + ); + return ExamplePage( + title: tdTitle(), + desc: '用于在不同功能模块之间进行快速切换,位于页面底部。', + exampleCodeGroup: 'bottomTabBar', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem( + ignoreCode: true, + desc: '纯文本标签栏', + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _textTypeTabBar), + ); + }), + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _textTypeTabBar3tabs), + ); + }), + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _textTypeTabBar4tabs), + ); + }), + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _textTypeTabBar5tabs), + ); + }), + ExampleItem( + ignoreCode: true, + desc: '图标加文本标签栏', + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _iconTextTypeTabBar), + ); + }), + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _iconTextTypeTabBar3tabs), + ); + }), + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _iconTextTypeTabBar4tabs), + ); + }), + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _iconTextTypeTabBar5tabs), + ); + }), + ExampleItem( + ignoreCode: true, + desc: '纯图标标签栏', + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _iconTypeTabBar), + ); + }), + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _iconTypeTabBar3tabs), + ); + }), + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _iconTypeTabBar4tabs), + ); + }), + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _iconTypeTabBar5tabs), + ); + }), + ExampleItem( + desc: '双层级文本标签栏', + builder: _expansionPanelTypeTabBar, + ), + ], + ), + ExampleModule(title: '组件样式', children: [ + ExampleItem( + ignoreCode: true, + desc: '弱选中标签栏', + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _weakSelectTextTabBar), + ); + }), + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _weakSelectIconTabBar), + ); + }), + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + padding: const EdgeInsets.only(bottom: 16), + child: CodeWrapper(builder: _weakSelectIconTextTabBar), + ); + }), + ExampleItem( + desc: '悬浮胶囊标签栏', + builder: _capsuleTabBar, + ), + ]), + ExampleModule(title: '组件事件', children: [ + ExampleItem( + desc: '长按触发', + builder: _capsuleTabBarOnLongPress, + ), + ]), + ], + test: [ + ExampleItem(desc: '自定义上边线样式', builder: _buildCustomTopStyle), + ExampleItem(desc: '自定义选择的背景颜色', builder: _customBgColor), + ExampleItem( + ignoreCode: true, + desc: '设置文本标签栏背景', + builder: (context) { + return CodeWrapper(builder: _customBgTypeTabBar); + }), + ExampleItem( + ignoreCode: true, + desc: '外部设置tabbar的选中项', + builder: (context) { + return CodeWrapper(builder: _setCurrentIndexToTabBar); + }), + ExampleItem( + ignoreCode: true, + desc: 'icon默认大小底部文字不溢出', + builder: (context) { + return CodeWrapper(builder: _iconTextTypeTabBarOverflow); + }), + ExampleItem(desc: 'onTap支持重复触发', builder: _allowMultipleTaps), + ExampleItem(desc: '支持水波纹效果', builder: _needInkWellTabBar), + ], + ); + } + + @Demo(group: 'bottomTabBar') + Widget _textTypeTabBar(BuildContext context) { + var _currentIndex = 0; + void _onTapTab(BuildContext context, String tabName, int currentIndex, + int currentSelectIndex) { + print('点击了 $tabName, 当前index: $currentIndex, 当前选择index: $currentSelectIndex'); + if (currentIndex == currentSelectIndex) { + TDToast.showText('$tabName 已经被选中了', context: context); + return; + } + TDToast.showText('点击了 $tabName', context: context); + } + + return TDBottomTabBar(TDBottomTabBarBasicType.text, + currentIndex: _currentIndex, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + _onTapTab(context, '标签1', 0, _currentIndex); + _currentIndex = 0; + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + _onTapTab(context, '标签2', 1, _currentIndex); + _currentIndex = 1; + }, + ), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _textTypeTabBar3tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.text, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _textTypeTabBar4tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.text, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _textTypeTabBar5tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.text, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _iconTextTypeTabBar(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _iconTextTypeTabBar3tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _iconTextTypeTabBar4tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _iconTextTypeTabBar5tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _iconTextTypeTabBarOverflow(BuildContext context) { + final selectedIcon = Icon( + TDIcons.app, + color: TDTheme.of(context).brandNormalColor, + ); + final unSelectedIcon = Icon( + TDIcons.app, + color: TDTheme.of(context).brandNormalColor, + ); + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: selectedIcon, + unselectedIcon: unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: selectedIcon, + unselectedIcon: unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: selectedIcon, + unselectedIcon: unSelectedIcon, + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _iconTypeTabBar(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.icon, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }) + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _iconTypeTabBar3tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.icon, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _iconTypeTabBar4tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.icon, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _iconTypeTabBar5tabs(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.icon, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _expansionPanelTypeTabBar(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.expansionPanel, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '展开项', + onTap: () { + onTapTab(context, '展开项'); + }, + popUpButtonConfig: TDBottomTabBarPopUpBtnConfig( + popUpDialogConfig: TDBottomTabBarPopUpShapeConfig( + radius: 10, + arrowWidth: 16, + arrowHeight: 8, + ), + items: [ + '展开项一', + '展开项二', + '展开项三', + ] + .reversed + .map((e) => PopUpMenuItem( + value: e, + itemWidget: SizedBox( + //height: 30, + child: Text( + e, + style: TextStyle( + color: TDTheme.of(context).fontGyColor1, + fontSize: 16), + ), + ), + )) + .toList(), + onChanged: (v) { + TDToast.showText('点击了 $v', context: context); + })), + ], + ); + } + + @Demo(group: 'bottomTabBar') + Widget _weakSelectTextTabBar(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.text, + componentType: TDBottomTabBarComponentType.normal, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + badgeConfig: BadgeConfig( + showBadge: true, + tdBadge: const TDBadge(TDBadgeType.redPoint), + badgeTopOffset: -2, + badgeRightOffset: -10, + ), + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ], + ); + } + + @Demo(group: 'bottomTabBar') + Widget _weakSelectIconTabBar(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.icon, + componentType: TDBottomTabBarComponentType.normal, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + badgeConfig: BadgeConfig( + showBadge: true, + tdBadge: const TDBadge(TDBadgeType.redPoint), + badgeTopOffset: -2, + badgeRightOffset: -10, + ), + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ], + ); + } + + @Demo(group: 'bottomTabBar') + Widget _weakSelectIconTextTabBar(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.iconText, + componentType: TDBottomTabBarComponentType.normal, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + badgeConfig: BadgeConfig( + showBadge: true, + tdBadge: const TDBadge(TDBadgeType.redPoint), + badgeTopOffset: -2, + badgeRightOffset: -10, + ), + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ], + ); + } + + @Demo(group: 'bottomTabBar') + Widget _capsuleTabBar(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.iconText, + componentType: TDBottomTabBarComponentType.label, + outlineType: TDBottomTabBarOutlineType.capsule, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ], + ); + } + + @Demo(group: 'bottomTabBar') + Widget _capsuleTabBarOnLongPress(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.iconText, + componentType: TDBottomTabBarComponentType.label, + outlineType: TDBottomTabBarOutlineType.capsule, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + onLongPress: () { + print('长按了标签1'); + TDToast.showText('长按了标签1', context: context); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + onLongPress: () { + TDToast.showText('长按了标签2', context: context); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + onLongPress: () { + TDToast.showText('长按了标签3', context: context); + }, + ), + ], + ); + } + + @Demo(group: 'bottomTabBar') + Widget _buildCustomTopStyle(BuildContext context) { + return TDBottomTabBar( + TDBottomTabBarBasicType.iconText, + topBorder: const BorderSide(color: Colors.red, width: 5), + barHeight: 61, + componentType: TDBottomTabBarComponentType.normal, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + badgeConfig: BadgeConfig( + showBadge: true, + tdBadge: const TDBadge(TDBadgeType.redPoint), + badgeTopOffset: -2, + badgeRightOffset: -10, + ), + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + tabText: '标签', + onTap: () { + onTapTab(context, '标签3'); + }, + ), + ], + ); + } + + @Demo(group: 'bottomTabBar') + Widget _customBgColor(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + useVerticalDivider: false, + selectedBgColor: TDTheme.of(context).errorColor3, + unselectedBgColor: TDTheme.of(context).grayColor3, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _customBgTypeTabBar(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.text, + backgroundColor: TDTheme.of(context).successColor6, + selectedBgColor: TDTheme.of(context).errorColor1, + unselectedBgColor: TDTheme.of(context).brandColor1, + useVerticalDivider: false, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + unselectTabTextStyle: + TextStyle(color: TDTheme.of(context).fontGyColor1), + onTap: () { + onTapTab(context, '标签1'); + }, + ), + ]); + } + + var currentIndex = 0; + @Demo(group: 'bottomTabBar') + Widget _setCurrentIndexToTabBar(BuildContext context) { + return SizedBox( + height: 200, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: PageView( + children: const [ + Center( + child: TDText('页面1,手指左滑查看页面2'), + ), + Center( + child: TDText('页面2,手指右滑查看页面1'), + ), + ], + onPageChanged: (index) { + setState(() { + // 修改选择index + currentIndex = index; + }); + }, + )), + TDBottomTabBar( + // 设置选择index + currentIndex: currentIndex, + TDBottomTabBarBasicType.icon, + useVerticalDivider: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }), + TDBottomTabBarTabConfig( + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }) + ]) + ], + ), + ); + } + + @Demo(group: 'bottomTabBar') + Widget _allowMultipleTaps(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.text, useVerticalDivider: false, navigationTabs: [ + TDBottomTabBarTabConfig( + allowMultipleTaps: true, + tabText: '支持重复点击', + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '不支持重复点击', + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } + + @Demo(group: 'bottomTabBar') + Widget _needInkWellTabBar(BuildContext context) { + return TDBottomTabBar(TDBottomTabBarBasicType.iconText, + needInkWell: true, + navigationTabs: [ + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签1'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + TDBottomTabBarTabConfig( + tabText: '标签', + selectedIcon: _selectedIcon, + unselectedIcon: _unSelectedIcon, + onTap: () { + onTapTab(context, '标签2'); + }, + ), + ]); + } +} diff --git a/tdesign-component/example/lib/page/td_button_page.dart b/tdesign-component/example/lib/page/td_button_page.dart new file mode 100644 index 000000000..e38f41e5d --- /dev/null +++ b/tdesign-component/example/lib/page/td_button_page.dart @@ -0,0 +1,1336 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +class TDButtonPage extends StatefulWidget { + const TDButtonPage({Key? key}) : super(key: key); + + @override + State createState() => _TDButtonPageState(); +} + +class _TDButtonPageState extends State { + void onTap() { + TDToast.showText('点击了按钮', context: context); + } + + void onLongPress() { + TDToast.showText('长按了按钮', context: context); + } + + @override + Widget build(BuildContext context) { + return Container( + color: TDTheme.of(context).grayColor2, + child: ExamplePage( + title: tdTitle(), + desc: '用于开启一个闭环的操作任务,如“删除”对象、“购买”商品等。', + exampleCodeGroup: 'button', + // padding: const EdgeInsets.only(top: 8, bottom: 8, ), + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + ignoreCode: true, + desc: '基础按钮', + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 8), + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _buildPrimaryFillButton, + methodName: '_buildPrimaryFillButton', + ), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _buildLightFillButton, + methodName: '_buildLightFillButton', + ), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildDefaultFillButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildPrimaryStrokeButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildPrimaryTextButton), + ), + ], + ), + ); + }), + ExampleItem( + ignoreCode: true, + desc: '图标按钮', + center: false, + builder: (context) { + return Container( + padding: const EdgeInsets.only(left: 8, right: 8), + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildRectangleIconButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper(builder: _buildSquareIconButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildLoadingIconButton), + ) + ], + ), + ); + }), + ExampleItem( + ignoreCode: true, + desc: '幽灵按钮', + builder: (context) { + return Container( + alignment: Alignment.topLeft, + color: TDTheme.of(context).grayColor14, + padding: const EdgeInsets.only(left: 8, right: 8), + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildPrimaryGhostButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildDangerGhostButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildDefaultGhostButton), + ), + ], + ), + ); + }), + ExampleItem( + ignoreCode: true, + desc: '组合按钮', + builder: (_) => + CodeWrapper(builder: _buildCombinationButtons)), + ExampleItem(desc: '通栏按钮', builder: _buildFilledFillButton), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem( + ignoreCode: true, + desc: '按钮禁用状态', + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 8), + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _buildDisablePrimaryFillButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _buildDisableLightFillButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _buildDisableDefaultFillButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _buildDisablePrimaryStrokeButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _buildDisablePrimaryTextButton), + ), + ], + ), + ); + }), + ]), + ExampleModule(title: '组件主题', children: [ + ExampleItem( + ignoreCode: true, + desc: '按钮尺寸', + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 10), + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.all(6), + child: CodeWrapper(builder: _buildLargeButton), + ), + Container( + margin: const EdgeInsets.all(6), + child: CodeWrapper(builder: _buildMediumButton), + ), + Container( + margin: const EdgeInsets.all(6), + child: CodeWrapper(builder: _buildSmallButton), + ), + Container( + margin: const EdgeInsets.all(6), + child: CodeWrapper(builder: _buildExtraSmallButton), + ), + ], + ), + ); + }), + ExampleItem( + ignoreCode: true, + desc: '按钮形状', + builder: (context) { + return Container( + alignment: Alignment.topLeft, + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.only( + left: 16, right: 6, top: 6), + child: CodeWrapper( + builder: _buildPrimaryFillButton, + ), + ), + Container( + margin: const EdgeInsets.all(6), + child: CodeWrapper(builder: _buildSquareIconButton), + ), + Container( + margin: const EdgeInsets.all(6), + child: CodeWrapper(builder: _buildRoundButton), + ), + Container( + margin: const EdgeInsets.only( + right: 16, left: 6, top: 6), + child: CodeWrapper(builder: _buildCircleButton), + ), + Container( + margin: const EdgeInsets.only(top: 10), + child: CodeWrapper(builder: _buildFilledButton), + ) + ], + ), + ); + }), + ExampleItem( + ignoreCode: true, + desc: '按钮主题', + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 8), + child: Wrap( + children: [ + /// 默认主题 + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildDefaultFillButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildDefaultStrokeButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildDefaultTextButton), + ), + + /// primary主题 + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _buildPrimaryFillButton, + ), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildPrimaryStrokeButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildPrimaryTextButton), + ), + + /// danger主题 + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper(builder: _buildDangerFillButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildDangerStrokeButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper(builder: _buildDangerTextButton), + ), + + /// light主题 + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper(builder: _buildLightFillButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: + CodeWrapper(builder: _buildLightStrokeButton), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _buildLightTextButton, + ), + ), + ], + ), + ); + }), + ]), + ], + test: [ + ExampleItem( + ignoreCode: true, + desc: '测试child', + builder: (context) { + return CodeWrapper(builder: _buildChildTestButton); + }), + ExampleItem( + ignoreCode: true, + desc: '通栏按钮测试', + builder: (context) { + return Container( + color: Colors.grey, + padding: const EdgeInsets.only(top: 16, bottom: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: const [ + TDButton( + isBlock: true, + text: '填充block按钮', + theme: TDButtonTheme.primary, + ), + SizedBox( + height: 16, + ), + TDButton( + isBlock: true, + text: '描边block按钮', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + ), + SizedBox( + height: 16, + ), + TDButton( + isBlock: true, + text: '文字block按钮', + type: TDButtonType.text, + theme: TDButtonTheme.primary, + ), + SizedBox( + height: 16, + ), + TDButton( + isBlock: true, + text: '幽灵block按钮', + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + ), + ], + ), + ); + }), + ExampleItem( + ignoreCode: true, + desc: '各种按钮状态测试', + builder: _buildStatusDisplay), + ExampleItem( + ignoreCode: true, + desc: '按钮中路由跳转', + builder: (context) { + return TDButton( + text: '点击跳转', + size: TDButtonSize.large, + // type: TDButtonType.text, + shape: TDButtonShape.rectangle, + onTap: () async { + var result = await Navigator.of(context) + .pushNamedAndRemoveUntil('divider', (router) { + return true; + }); + print('pushNamedAndRemoveUntil result: $result'); + }, + ); + }), + ExampleItem( + ignoreCode: true, + desc: '图标在文字右侧', + builder: (context) { + return CodeWrapper(builder: _buildRightIconButton); + }), + ], + )); + } + + @Demo(group: 'button') + TDButton _buildLightTextButton(BuildContext context) { + return const TDButton( + text: '文字按钮', + size: TDButtonSize.large, + type: TDButtonType.text, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.light, + ); + } + + @Demo(group: 'button') + TDButton _buildLightStrokeButton(BuildContext context) { + return const TDButton( + text: '描边按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.light, + ); + } + + @Demo(group: 'button') + TDButton _buildDangerTextButton(BuildContext context) { + return const TDButton( + text: '文字按钮', + size: TDButtonSize.large, + type: TDButtonType.text, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.danger, + ); + } + + @Demo(group: 'button') + TDButton _buildDangerStrokeButton(BuildContext context) { + return const TDButton( + text: '描边按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.danger, + ); + } + + @Demo(group: 'button') + TDButton _buildDangerFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.danger, + ); + } + + @Demo(group: 'button') + TDButton _buildDefaultTextButton(BuildContext context) { + return const TDButton( + text: '文字按钮', + size: TDButtonSize.large, + type: TDButtonType.text, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.defaultTheme, + ); + } + + @Demo(group: 'button') + TDButton _buildDefaultStrokeButton(BuildContext context) { + return const TDButton( + text: '描边按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.defaultTheme, + ); + } + + @Demo(group: 'button') + TDButton _buildFilledButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.filled, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildCircleButton(BuildContext context) { + return const TDButton( + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.circle, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildRoundButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.round, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildExtraSmallButton(BuildContext context) { + return const TDButton( + text: '按钮28', + size: TDButtonSize.extraSmall, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildSmallButton(BuildContext context) { + return const TDButton( + text: '按钮32', + size: TDButtonSize.small, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildMediumButton(BuildContext context) { + return const TDButton( + text: '按钮40', + size: TDButtonSize.medium, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildLargeButton(BuildContext context) { + return const TDButton( + text: '按钮48', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildDisablePrimaryTextButton(BuildContext context) { + return const TDButton( + text: '文字按钮', + size: TDButtonSize.large, + type: TDButtonType.text, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + disabled: true, + ); + } + + @Demo(group: 'button') + TDButton _buildDisablePrimaryStrokeButton(BuildContext context) { + return const TDButton( + text: '描边按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + disabled: true, + ); + } + + @Demo(group: 'button') + TDButton _buildDisableDefaultFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.defaultTheme, + disabled: true, + ); + } + + @Demo(group: 'button') + TDButton _buildDisableLightFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.light, + disabled: true, + ); + } + + @Demo(group: 'button') + TDButton _buildDisablePrimaryFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + disabled: true, + ); + } + + @Demo(group: 'button') + TDButton _buildFilledFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + theme: TDButtonTheme.primary, + isBlock: true, + ); + } + + @Demo(group: 'button') + TDButton _buildDefaultGhostButton(BuildContext context) { + return const TDButton( + text: '幽灵按钮', + size: TDButtonSize.large, + type: TDButtonType.ghost, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.defaultTheme, + ); + } + + @Demo(group: 'button') + TDButton _buildDangerGhostButton(BuildContext context) { + return const TDButton( + text: '幽灵按钮', + size: TDButtonSize.large, + type: TDButtonType.ghost, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.danger, + ); + } + + @Demo(group: 'button') + TDButton _buildPrimaryGhostButton(BuildContext context) { + return const TDButton( + text: '幽灵按钮', + size: TDButtonSize.large, + type: TDButtonType.ghost, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildSquareIconButton(BuildContext context) { + return const TDButton( + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.square, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildLoadingIconButton(BuildContext context) { + return TDButton( + text: '加载中', + iconWidget: TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + iconColor: TDTheme.of(context).whiteColor1, + ), + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildRectangleIconButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildPrimaryTextButton(BuildContext context) { + return const TDButton( + text: '文字按钮', + size: TDButtonSize.large, + type: TDButtonType.text, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildPrimaryStrokeButton(BuildContext context) { + return const TDButton( + text: '描边按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildDefaultFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.defaultTheme, + ); + } + + @Demo(group: 'button') + @Demo(group: 'button') + TDButton _buildPrimaryFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + ); + } + + @Demo(group: 'button') + TDButton _buildLightFillButton(BuildContext context) { + return const TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.light, + ); + } + + @Demo(group: 'button') + Widget _buildCombinationButtons(BuildContext context) { + return Row( + children: const [ + SizedBox( + width: 16, + ), + Expanded( + child: TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.light, + )), + SizedBox( + width: 16, + ), + Expanded( + child: TDButton( + text: '填充按钮', + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + )), + SizedBox( + width: 16, + ), + ], + ); + } + + @Demo(group: 'button') + Widget _buildChildTestButton(BuildContext context) { + return TDButton( + child: Container( + height: 24, + width: 24, + color: Colors.red, + ), + ); + } + + @Demo(group: 'button') + Widget _buildRightIconButton(BuildContext context) { + return Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + TDButton( + text: '填充按钮', + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + iconPosition: TDButtonIconPosition.right, + ), + TDButton( + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + iconPosition: TDButtonIconPosition.right, + ), + TDButton( + text: '间距20', + icon: TDIcons.app, + size: TDButtonSize.large, + type: TDButtonType.fill, + shape: TDButtonShape.rectangle, + theme: TDButtonTheme.primary, + iconPosition: TDButtonIconPosition.right, + iconTextSpacing: 20, + ) + ], + ), + ); + } + + Widget _buildStatusDisplay(BuildContext context) { + return ListView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + children: [ + /// fill + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + style: TDButtonStyle.generateFillStyleByTheme( + context, TDButtonTheme.primary, TDButtonStatus.active), + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + disabled: true, + ), + ], + ), + ), + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + style: TDButtonStyle.generateFillStyleByTheme( + context, TDButtonTheme.light, TDButtonStatus.active), + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + disabled: true, + ), + ], + ), + ), + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + style: TDButtonStyle.generateFillStyleByTheme( + context, TDButtonTheme.defaultTheme, TDButtonStatus.active), + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + disabled: true, + ), + ], + ), + ), + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + style: TDButtonStyle.generateFillStyleByTheme( + context, TDButtonTheme.danger, TDButtonStatus.active), + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + disabled: true, + ), + ], + ), + ), + + /// outline + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + style: TDButtonStyle.generateOutlineStyleByTheme( + context, TDButtonTheme.primary, TDButtonStatus.active), + type: TDButtonType.outline, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + disabled: true, + type: TDButtonType.outline, + ), + ], + ), + ), + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + type: TDButtonType.outline, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + style: TDButtonStyle.generateOutlineStyleByTheme( + context, TDButtonTheme.light, TDButtonStatus.active), + type: TDButtonType.outline, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + disabled: true, + type: TDButtonType.outline, + ), + ], + ), + ), + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + type: TDButtonType.outline, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + style: TDButtonStyle.generateOutlineStyleByTheme( + context, TDButtonTheme.defaultTheme, TDButtonStatus.active), + type: TDButtonType.outline, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + disabled: true, + type: TDButtonType.outline, + ), + ], + ), + ), + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + type: TDButtonType.outline, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + style: TDButtonStyle.generateOutlineStyleByTheme( + context, TDButtonTheme.danger, TDButtonStatus.active), + type: TDButtonType.outline, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + disabled: true, + type: TDButtonType.outline, + ), + ], + ), + ), + + /// text + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + type: TDButtonType.text, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + style: TDButtonStyle.generateTextStyleByTheme( + context, TDButtonTheme.primary, TDButtonStatus.active), + type: TDButtonType.text, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + disabled: true, + type: TDButtonType.text, + ), + ], + ), + ), + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + type: TDButtonType.text, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + style: TDButtonStyle.generateTextStyleByTheme( + context, TDButtonTheme.light, TDButtonStatus.active), + type: TDButtonType.text, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + disabled: true, + type: TDButtonType.text, + ), + ], + ), + ), + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + type: TDButtonType.text, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + style: TDButtonStyle.generateTextStyleByTheme( + context, TDButtonTheme.defaultTheme, TDButtonStatus.active), + type: TDButtonType.text, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + disabled: true, + type: TDButtonType.text, + ), + ], + ), + ), + Container( + margin: const EdgeInsets.all(16), + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + type: TDButtonType.text, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + style: TDButtonStyle.generateTextStyleByTheme( + context, TDButtonTheme.danger, TDButtonStatus.active), + type: TDButtonType.text, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + disabled: true, + type: TDButtonType.text, + ), + ], + ), + ), + + /// ghost + Container( + padding: const EdgeInsets.all(16), + color: Colors.black, + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + type: TDButtonType.ghost, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + style: TDButtonStyle.generateGhostStyleByTheme( + context, TDButtonTheme.primary, TDButtonStatus.active), + type: TDButtonType.ghost, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.primary, + disabled: true, + type: TDButtonType.ghost, + ), + ], + ), + ), + Container( + padding: const EdgeInsets.all(16), + color: Colors.black, + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + type: TDButtonType.ghost, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + style: TDButtonStyle.generateGhostStyleByTheme( + context, TDButtonTheme.light, TDButtonStatus.active), + type: TDButtonType.ghost, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.light, + disabled: true, + type: TDButtonType.ghost, + ), + ], + ), + ), + Container( + padding: const EdgeInsets.all(16), + color: Colors.black, + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + type: TDButtonType.ghost, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + style: TDButtonStyle.generateGhostStyleByTheme( + context, TDButtonTheme.defaultTheme, TDButtonStatus.active), + type: TDButtonType.ghost, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.defaultTheme, + disabled: true, + type: TDButtonType.ghost, + ), + ], + ), + ), + Container( + padding: const EdgeInsets.all(16), + color: Colors.black, + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + type: TDButtonType.ghost, + ), + TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + style: TDButtonStyle.generateGhostStyleByTheme( + context, TDButtonTheme.danger, TDButtonStatus.active), + type: TDButtonType.ghost, + ), + const TDButton( + icon: TDIcons.app, + text: 'Button', + theme: TDButtonTheme.danger, + disabled: true, + type: TDButtonType.ghost, + ), + ], + ), + ), + ], + ); + } +} diff --git a/tdesign-component/example/lib/page/td_calendar_page.dart b/tdesign-component/example/lib/page/td_calendar_page.dart new file mode 100644 index 000000000..3cd81ff6b --- /dev/null +++ b/tdesign-component/example/lib/page/td_calendar_page.dart @@ -0,0 +1,432 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDCalendarPage extends StatelessWidget { + const TDCalendarPage({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: TDTheme.of(context).grayColor2, + child: ExamplePage( + title: tdTitle(context), + desc: '按照日历形式展示数据或日期的容器。', + exampleCodeGroup: 'calendar', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + ignoreCode: true, + center: false, + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildSimple); + }, + ), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem( + desc: '可以自由定义想要的风格', + ignoreCode: true, + center: false, + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildStyle); + }, + ), + ExampleItem( + desc: '自定义日期单元格', + ignoreCode: true, + center: false, + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildCustomCell); + }, + ), + ExampleItem( + desc: '不使用Popup', + ignoreCode: true, + center: false, + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildBlock); + }, + ), + ]), + ], + test: const [], + ), + ); + } +} + +@Demo(group: 'calendar') +Widget _buildSimple(BuildContext context) { + final size = MediaQuery.of(context).size; + final selected = ValueNotifier>([DateTime.now().millisecondsSinceEpoch + 30 * 24 * 60 * 60 * 1000]); + return ValueListenableBuilder( + valueListenable: selected, + builder: (context, value, child) { + final date = DateTime.fromMillisecondsSinceEpoch(value[0]); + return TDCellGroup( + cells: [ + TDCell( + title: '单个选择日历', + arrow: true, + note: '${date.year}-${date.month}-${date.day}', + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + onConfirm: (value) { + print('onConfirm:$value'); + selected.value = value; + }, + onClose: () { + print('onClose'); + }, + child: TDCalendar( + title: '请选择日期', + value: value, + height: size.height * 0.6 + 176, + onCellClick: (value, type, tdate) { + print('onCellClick:$value'); + }, + onCellLongPress: (value, type, tdate) { + print('onCellLongPress:$value'); + }, + onHeaderClick: (index, week) { + print('onHeaderClick:$week'); + }, + onChange: (value) { + print('onChange:$value'); + }, + ), + ); + }, + ), + TDCell( + title: '多个选择日历', + arrow: true, + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + child: TDCalendar( + title: '请选择日期', + type: CalendarType.multiple, + value: [DateTime.now().millisecondsSinceEpoch], + height: size.height * 0.6 + 176, + ), + ); + }, + ), + TDCell( + title: '区间选择日历', + arrow: true, + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + child: TDCalendar( + title: '请选择日期区间', + type: CalendarType.range, + value: [ + DateTime.now().millisecondsSinceEpoch, + DateTime.now().add(const Duration(days: 6)).millisecondsSinceEpoch, + ], + height: size.height * 0.6 + 176, + ), + ); + }, + ), + TDCell( + title: '单个选择日历和时间', + arrow: true, + note: '${date.year}-${date.month}-${date.day} ${date.hour}:${date.minute}', + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + onConfirm: (value) { + print('onConfirm:$value'); + selected.value = value; + }, + onClose: () { + print('onClose'); + }, + child: TDCalendar( + title: '请选择日期和时间', + value: value, + height: size.height * 0.92, + useTimePicker: true, + // pickerHeight: 100, + // pickerItemCount: 2, + onCellClick: (value, type, tdate) { + print('onCellClick:$value'); + }, + onCellLongPress: (value, type, tdate) { + print('onCellLongPress:$value'); + }, + onHeaderClick: (index, week) { + print('onHeaderClick:$week'); + }, + onChange: (value) { + print('onChange:$value'); + }, + ), + ); + }, + ), + TDCell( + title: '区间选择日历和时间', + arrow: true, + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + onConfirm: (value) { + print('onConfirm:$value'); + }, + onClose: () { + print('onClose'); + }, + child: TDCalendar( + title: '请选择日期和时间区间', + height: size.height * 0.92, + type: CalendarType.range, + value: [ + DateTime.now().millisecondsSinceEpoch, + DateTime.now().add(const Duration(days: 3)).millisecondsSinceEpoch, + ], + useTimePicker: true, + onCellClick: (value, type, tdate) { + print('onCellClick:$value'); + }, + onCellLongPress: (value, type, tdate) { + print('onCellLongPress:$value'); + }, + onHeaderClick: (index, week) { + print('onHeaderClick:$week'); + }, + onChange: (value) { + print('onChange:$value'); + }, + ), + ); + }, + ), + ], + ); + }, + ); +} + +@Demo(group: 'calendar') +Widget _buildStyle(BuildContext context) { + final size = MediaQuery.of(context).size; + const map = { + 1: '初一', + 2: '初二', + 3: '初三', + 14: '情人节', + 15: '元宵节', + }; + return TDCellGroup( + cells: [ + TDCell( + title: '自定义文案', + arrow: true, + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + child: TDCalendar( + title: '请选择日期', + height: size.height * 0.6 + 176, + minDate: DateTime(2022, 1, 1).millisecondsSinceEpoch, + maxDate: DateTime(2022, 2, 15).millisecondsSinceEpoch, + format: (day) { + day?.suffix = '¥60'; + if (day?.date.month == 2) { + if (map.keys.contains(day?.date.day)) { + day?.suffix = '¥100'; + day?.prefix = map[day.date.day]; + day?.style = TextStyle( + fontSize: TDTheme.of(context).fontTitleMedium?.size, + height: TDTheme.of(context).fontTitleMedium?.height, + fontWeight: TDTheme.of(context).fontTitleMedium?.fontWeight, + color: TDTheme.of(context).errorColor6, + ); + if (day?.typeNotifier.value == DateSelectType.selected) { + day?.style = day.style?.copyWith(color: TDTheme.of(context).fontWhColor1); + } + } + } + return null; + }, + ), + ); + }, + ), + TDCell( + title: '自定义按钮', + arrow: true, + onClick: (cell) { + late final TDCalendarPopup calendar; + calendar = TDCalendarPopup( + context, + visible: true, + confirmBtn: Padding( + padding: EdgeInsets.symmetric(vertical: TDTheme.of(context).spacer16), + child: TDButton( + theme: TDButtonTheme.danger, + shape: TDButtonShape.round, + text: 'ok', + isBlock: true, + size: TDButtonSize.large, + onTap: () { + print(calendar.selected); + calendar.close(); + }, + ), + ), + child: TDCalendar( + title: '请选择日期', + value: [DateTime.now().millisecondsSinceEpoch], + height: size.height * 0.6 + 176, + ), + ); + }, + ), + TDCell( + title: '自定义日期区间', + arrow: true, + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + child: TDCalendar( + title: '请选择日期', + minDate: DateTime(2000, 1, 1).millisecondsSinceEpoch, + maxDate: DateTime(3000, 1, 1).millisecondsSinceEpoch, + value: [DateTime(2024, 10, 1).millisecondsSinceEpoch], + height: size.height * 0.6 + 176, + ), + ); + }, + ), + ], + ); +} + +@Demo(group: 'calendar') +Widget _buildBlock(BuildContext context) { + final size = MediaQuery.of(context).size; + final selected = ValueNotifier>([DateTime.now().millisecondsSinceEpoch + 30 * 24 * 60 * 60 * 1000]); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + SizedBox(width: TDTheme.of(context).spacer16), + TDButton( + text: '加一个月', + size: TDButtonSize.small, + theme: TDButtonTheme.primary, + onTap: () { + selected.value = [selected.value[0] + 30 * 24 * 60 * 60 * 1000]; + }), + SizedBox(width: TDTheme.of(context).spacer16), + TDButton( + text: '减一个月', + size: TDButtonSize.small, + theme: TDButtonTheme.primary, + onTap: () { + selected.value = [selected.value[0] - 30 * 24 * 60 * 60 * 1000]; + }), + ], + ), + SizedBox(height: TDTheme.of(context).spacer16), + ValueListenableBuilder( + valueListenable: selected, + builder: (context, value, child) { + return TDCalendar( + title: '请选择日期', + value: value, + height: size.height * 0.6 + 176, + animateTo: true, + ); + }, + ), + ], + ); +} + +@Demo(group: 'calendar') +Widget _buildCustomCell(BuildContext context) { + final size = MediaQuery.of(context).size; + final selected = ValueNotifier>([DateTime.now().millisecondsSinceEpoch + 30 * 24 * 60 * 60 * 1000]); + return ValueListenableBuilder( + valueListenable: selected, + builder: (context, value, child) { + final date = DateTime.fromMillisecondsSinceEpoch(value[0]); + return TDCellGroup( + cells: [ + TDCell( + title: '自定义日期单元格', + arrow: true, + note: '${date.year}-${date.month}-${date.day}', + onClick: (cell) { + TDCalendarPopup( + context, + visible: true, + onConfirm: (value) { + print('onConfirm:$value'); + selected.value = value; + }, + onClose: () { + print('onClose'); + }, + child: TDCalendar( + title: '请选择日期', + value: value, + height: size.height * 0.6 + 176, + onCellClick: (value, type, tdate) { + print('onCellClick:$value'); + }, + onCellLongPress: (value, type, tdate) { + print('onCellLongPress:$value'); + }, + onHeaderClick: (index, week) { + print('onHeaderClick:$week'); + }, + onChange: (value) { + print('onChange:$value'); + }, + cellWidget: (context, tdate, selectType) { + if (selectType == DateSelectType.selected) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('${tdate.date.day}', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white)), + Text('文案文案', style: TextStyle(fontSize: 6, color: Colors.white)), + Text('自定义', style: TextStyle(fontSize: 12, color: Colors.white)), + ], + ); + } + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('${tdate.date.day}', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)), + Text('文案文案', style: TextStyle(fontSize: 8)), + Text('自定义', style: TextStyle(fontSize: 8)), + ], + ); + } + ), + ); + }, + ), + ], + ); + }, + ); +} diff --git a/tdesign-component/example/lib/page/td_cascader_page.dart b/tdesign-component/example/lib/page/td_cascader_page.dart new file mode 100644 index 000000000..8c1d6a79f --- /dev/null +++ b/tdesign-component/example/lib/page/td_cascader_page.dart @@ -0,0 +1,584 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +class TDCascaderPage extends StatefulWidget { + const TDCascaderPage({super.key}); + + @override + State createState() => _TDCascaderPageState(); +} + +class _TDCascaderPageState extends State { + String? _initData; + String _selected_1 = ''; + final List _data = [ + { + 'label': '北京市', + 'value': '110000', + 'children': [ + { + 'value': '110100', + 'label': '北京市', + 'children': [ + {'value': '110101', 'label': '东城区'}, + {'value': '1101022', 'label': '东区'}, + {'value': '110102', 'label': '西城区'}, + {'value': '110105', 'label': '朝阳区'}, + {'value': '110106', 'label': '丰台区'}, + {'value': '110107', 'label': '石景山区'}, + {'value': '110108', 'label': '海淀区'}, + {'value': '110109', 'label': '门头沟区'}, + ], + }, + ], + }, + { + 'label': '天津市', + 'value': '120000', + 'children': [ + { + 'value': '120100', + 'label': '天津市', + 'children': [ + { + 'value': '120101', + 'label': '和平区', + }, + { + 'value': '120102', + 'label': '河东区', + }, + { + 'value': '120103', + 'label': '河西区', + }, + { + 'value': '120104', + 'label': '南开区', + }, + { + 'value': '120105', + 'label': '河北区', + }, + { + 'value': '120106', + 'label': '红桥区', + }, + { + 'value': '120110', + 'label': '东丽区', + }, + { + 'value': '120111', + 'label': '西青区', + }, + { + 'value': '120112', + 'label': '津南区', + }, + ], + }, + ], + }, + ]; + + String? _initData_2; + String _selected_2 = ''; + final List _data_2 = [ + { + 'label': '北京市', + 'value': '110000', + 'segmentValue': 'B', + 'children': [ + { + 'value': '110100', + 'label': '北京市', + 'segmentValue': 'B', + 'children': [ + { + 'value': '110101', + 'label': '东城区', + 'segmentValue': 'D', + }, + {'value': '1101022', 'label': '东区', 'segmentValue': 'D'}, + {'value': '110102', 'label': '西城区', 'segmentValue': 'X'}, + {'value': '110105', 'label': '朝阳区', 'segmentValue': 'C'}, + {'value': '110106', 'label': '丰台区', 'segmentValue': 'F'}, + {'value': '110107', 'label': '石景山区', 'segmentValue': 'S'}, + {'value': '110108', 'label': '海淀区', 'segmentValue': 'H'}, + {'value': '110109', 'label': '门头沟区', 'segmentValue': 'M'}, + ], + }, + ], + }, + { + 'label': '天津市', + 'value': '120000', + 'segmentValue': 'T', + 'children': [ + { + 'value': '120100', + 'label': '天津市', + 'segmentValue': 'T', + 'children': [ + { + 'value': '120101', + 'label': '和平区', + 'segmentValue': 'H', + }, + {'value': '120102', 'label': '河东区', 'segmentValue': 'H'}, + {'value': '120103', 'label': '河西区', 'segmentValue': 'H'}, + {'value': '120104', 'label': '南开区', 'segmentValue': 'N'}, + {'value': '120105', 'label': '河北区', 'segmentValue': 'H'}, + {'value': '120106', 'label': '红桥区', 'segmentValue': 'H'}, + {'value': '120110', 'label': '东丽区', 'segmentValue': 'D'}, + {'value': '120111', 'label': '西青区', 'segmentValue': 'X'}, + {'value': '120112', 'label': '津南区', 'segmentValue': 'J'}, + ], + }, + ], + }, + ]; + + String? _initData_3; + String _selected_3 = ''; + final List _data_3 = [ + { + 'label': '技术部门', + 'value': '110000', + 'segmentValue': 'J', + 'children': [ + { + 'value': '110100', + 'label': '部门一', + 'segmentValue': 'B', + 'children': [ + {'value': '110101', 'label': '洪磊', 'segmentValue': 'H'}, + {'value': '110102', 'label': '洪磊2', 'segmentValue': 'H'}, + {'value': '1101022', 'label': '洪磊3', 'segmentValue': 'H'}, + {'value': '110105', 'label': '洪磊4', 'segmentValue': 'H'}, + {'value': '110106', 'label': '郭天1', 'segmentValue': 'G'}, + {'value': '110107', 'label': '郭天2', 'segmentValue': 'G'}, + {'value': '110109', 'label': '冯笑1', 'segmentValue': 'F'}, + {'value': '110108', 'label': '郭天3', 'segmentValue': 'G'}, + ], + }, + { + 'value': '110200', + 'label': '部门二', + 'segmentValue': 'B', + 'children': [ + {'value': '110201', 'label': '洪磊', 'segmentValue': 'H'}, + {'value': '110205', 'label': '洪磊4', 'segmentValue': 'H'}, + {'value': '110206', 'label': '郭天1', 'segmentValue': 'G'}, + {'value': '110207', 'label': '郭天2', 'segmentValue': 'G'}, + {'value': '110208', 'label': '郭天3', 'segmentValue': 'G'}, + {'value': '110209', 'label': '冯笑1', 'segmentValue': 'F'}, + {'value': '110202', 'label': '洪磊2', 'segmentValue': 'H'}, + {'value': '1102022', 'label': '洪磊3', 'segmentValue': 'H'}, + ], + }, + ], + }, + { + 'label': '行政部门', + 'value': '120000', + 'segmentValue': 'X', + 'children': [ + { + 'value': '120100', + 'label': '部门一', + 'segmentValue': 'B', + 'children': [ + {'value': '120201', 'label': '洪磊', 'segmentValue': 'H'}, + {'value': '120205', 'label': '洪磊4', 'segmentValue': 'H'}, + {'value': '120206', 'label': '郭天1', 'segmentValue': 'G'}, + {'value': '120207', 'label': '郭天2', 'segmentValue': 'G'}, + {'value': '120208', 'label': '郭天3', 'segmentValue': 'G'}, + {'value': '120209', 'label': '冯笑1', 'segmentValue': 'F'}, + {'value': '120202', 'label': '洪磊2', 'segmentValue': 'H'}, + {'value': '1202022', 'label': '洪磊3', 'segmentValue': 'H'}, + ], + }, + ], + }, + ]; + + String? _initData_4; + String _selected_4 = ''; + final List _data_4 = [ + { + 'label': '技术部门', + 'value': '110000', + 'children': [ + { + 'value': '110100', + 'label': '部门一', + 'children': [ + {'value': '110201', 'label': '后勤部门', 'children':[ + { + 'value': '110301', 'label': '后勤A组','children':[ + { + 'value': '110401', 'label': '一组','children':[ + {'value': '110501', 'label': '洪磊',}, + {'value': '110502', 'label': '洪磊2'}, + {'value': '110506', 'label': '郭天1'}, + {'value': '110507', 'label': '郭天2'}, + {'value': '110508', 'label': '郭天3'}, + {'value': '110509', 'label': '冯笑1'}, + {'value': '1105022', 'label': '洪磊3'}, + {'value': '110505', 'label': '洪磊4'}, + ] + } + ] + } + ]}, + ], + }, + { + 'value': '120100', + 'label': '部门二', + 'children': [ + {'value': '120201', 'label': '后勤部门', 'children':[ + { + 'value': '120301', 'label': '后勤A组','children':[ + { + 'value': '120401', 'label': '一组','children':[ + {'value': '120501', 'label': '张雷1'}, + {'value': '120502', 'label': '张雷2'}, + {'value': '1205022', 'label': '张雷3'}, + {'value': '120505', 'label': '张雷4'}, + {'value': '120506', 'label': '张雷5'}, + {'value': '120507', 'label': '张雷6'}, + {'value': '120508', 'label': '张雷7'}, + {'value': '120509', 'label': '张雷8'}, + ] + } + ] + } + ]}, + ], + }, + ], + }, + ]; + + String? _initData_5; + String? _initData_6; + + @override + Widget build(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: ExamplePage( + title: tdTitle(), + exampleCodeGroup: 'cascader', + desc: '用于多层级数据的逐级选择', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '垂直级联选择器', builder: _buildVerticalCascader), + ExampleItem(desc: '垂直级联选择器-带字母定位', builder: _buildVerticalLetterCascader), + ExampleItem(desc: '水平级联选择器', builder: _buildHorizontalCascader), + ExampleItem(desc: '水平级联选择器-带字母定位', builder: _buildHorizontalLetterCascader), + ExampleItem(desc: '水平级联选择器-部门', builder: _buildHorizontalCompanyCascader), + ExampleItem(desc: '垂直级联选择器-部门', builder: _buildVerticalCompanyCascader), + ]), + ], + test: [ + ExampleItem(desc: '测试使用次标题', builder: _buildVerticalSubTitleCascader), + ExampleItem(desc: '垂直级联选择器-部门', builder: _buildTestVerticalCompanyCascader), + ExampleItem(desc: '选择任意项', builder: _buildSelectAnyItemCascader), + ], + ), + ); + } + + @Demo(group: 'cascader') + Widget _buildVerticalCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, title: '选择地址', data: _data, initialData: _initData, theme: 'step', + onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_1 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_1, '选择地区'), + ); + } + + @Demo(group: 'cascader') + Widget _buildVerticalLetterCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, title: '选择地址', data: _data_2, initialData: _initData_2, theme: 'step', + onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_2 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_2 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_2, '选择地区'), + ); + } + + @Demo(group: 'cascader') + Widget _buildHorizontalCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, + title: '选择地址', + subTitles: ['请选择省份', '请选择城市', '请选择区/县'], + data: _data, + initialData: _initData, + theme: 'tab', onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_1 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_1, '选择地区'), + ); + } + + @Demo(group: 'cascader') + Widget _buildHorizontalLetterCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, + title: '选择地址', + data: _data_2, + initialData: _initData_2, + isLetterSort: true, + theme: 'tab', onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_2 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_2 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_2, '选择地区'), + ); + } + + @Demo(group: 'cascader') + Widget _buildHorizontalCompanyCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, title: '选择部门人员', data: _data_3,isLetterSort: true, initialData: _initData_3, theme: 'tab', + onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_3 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_3 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_3, '选择部门人员'), + ); + } + + @Demo(group: 'cascader') + Widget _buildVerticalCompanyCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, title: '选择部门人员', data: _data_3,isLetterSort: true, initialData: _initData_3, theme: 'step', + onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_3 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_3 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_3, '选择部门人员'), + ); + } + + @Demo(group: 'cascader') + Widget _buildVerticalSubTitleCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, + title: '选择地址', + subTitles: ['请选择省份', '请选择城市', '请选择区/县'], + data: _data, + initialData: _initData_4, + theme: 'tab', onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_4 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_1 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_1, '选择地区'), + ); + } + + @Demo(group: 'cascader') + Widget _buildTestVerticalCompanyCascader(BuildContext context) { + return GestureDetector( + onTap: () { + TDCascader.showMultiCascader(context, + title: '选择部门人员', + data: _data_4, + initialData: _initData_5, + theme: 'step', onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initData_5 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_4 = result.join('/'); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + child: _buildSelectRow(context, _selected_4, '选择部门人员'), + ); + } + + @Demo(group: 'cascader') + Widget _buildSelectAnyItemCascader(BuildContext context) { + return GestureDetector( + onTap: () { + var action = (List selectData) { + if(selectData.isEmpty){ + TDToast.showText('请选择数据', context: context); + return; + } + setState(() { + var result = []; + var len = selectData.length; + _initData_6 = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_1 = result.join('/'); + }); + }; + TDCascader.showMultiCascader( + context, + title: '选择地址', + data: _data, + initialData: _initData_6, + action: TDCascaderAction(onConfirm: action), + onChange: action, + ); + }, + child: _buildSelectRow(context, _selected_1, '选择地区'), + ); + } + + + Widget _buildSelectRow(BuildContext context, String output, String title) { + return Container( + color: TDTheme.of(context).whiteColor1, + height: 56, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, top: 16, bottom: 16), + child: TDText( + title, + font: TDTheme.of(context).fontBodyLarge, + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only(right: 16, left: 16), + child: Row( + children: [ + Expanded( + child: TDText( + output, + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).fontGyColor3.withOpacity(0.4), + maxLines: 1, + overflow: TextOverflow.ellipsis, + )), + Padding( + padding: const EdgeInsets.only(left: 2), + child: Icon( + TDIcons.chevron_right, + color: TDTheme.of(context).fontGyColor3.withOpacity(0.4), + ), + ), + ], + ), + )), + ], + ), + const TDDivider( + margin: EdgeInsets.only( + left: 16, + ), + ) + ], + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_cell_page.dart b/tdesign-component/example/lib/page/td_cell_page.dart new file mode 100644 index 000000000..5a2d817c4 --- /dev/null +++ b/tdesign-component/example/lib/page/td_cell_page.dart @@ -0,0 +1,192 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDCellPage extends StatelessWidget { + const TDCellPage({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: TDTheme.of(context).grayColor2, + child: ExamplePage( + title: tdTitle(context), + desc: '一行内容/功能的垂直排列方式。一行项目左侧为主要内容展示区域,右侧可增加更多操作内容。', + exampleCodeGroup: 'cell', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + ignoreCode: true, + desc: '单行单元格', + center: false, + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildSimple); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '多行单元格', + center: false, + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildDesSimple); + }, + ), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem( + ignoreCode: true, + desc: '卡片单元格', + center: false, + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildCard); + }, + ), + ]), + ], + test: [ + ExampleItem( + ignoreCode: true, + desc: '自定义内边距-padding', + center: false, + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildPadding); + }, + ), + ], + )); + } +} + +@Demo(group: 'cell') +Widget _buildSimple(BuildContext context) { + // 可统一修改样式 + var style = TDCellStyle(context: context); + return TDCellGroup( + style: style, + cells: [ + // 可单独修改样式 + TDCell(arrow: true, title: '单行标题', style: TDCellStyle.cellStyle(context)), + TDCell(arrow: true, title: '单行标题', required: true, onClick: (cell) { + print('单行标题'); + }), + const TDCell(arrow: true, title: '单行标题', noteWidget: TDBadge(TDBadgeType.message, count: '8')), + const TDCell(arrow: false, title: '单行标题', rightIconWidget: TDSwitch(isOn: true)), + const TDCell(arrow: true, title: '单行标题', note: '辅助信息'), + const TDCell(arrow: true, title: '单行标题', leftIcon: TDIcons.lock_on), + const TDCell(arrow: false, title: '单行标题'), + ], + ); +} + +@Demo(group: 'cell') +Widget _buildDesSimple(BuildContext context) { + return const TDCellGroup( + cells: [ + TDCell(arrow: true, title: '单行标题', description: '一段很长很长的内容文字'), + TDCell( + arrow: true, + title: '单行标题', + description: '一段很长很长的内容文字', + required: true), + TDCell( + arrow: true, + title: '单行标题', + description: '一段很长很长的内容文字', + noteWidget: TDBadge(TDBadgeType.message, count: '8')), + TDCell( + arrow: false, + title: '单行标题', + description: '一段很长很长的内容文字', + rightIconWidget: TDSwitch(isOn: true)), + TDCell( + arrow: true, title: '单行标题', description: '一段很长很长的内容文字', note: '辅助信息'), + TDCell( + arrow: true, + title: '单行标题', + description: '一段很长很长的内容文字一段很长很长的内容文字一段很长很长的内', + leftIcon: TDIcons.lock_on), + TDCell( + arrow: false, + title: '单行标题', + description: '一段很长很长的内容文字一段很长很长的内容文字一段很长很长的内'), + TDCell( + arrow: false, + title: '多行高度不定,长文本自动换行,该选项的描述是一段很长的内容', + description: '一段很长很长的内容文字一段很长很长的内容文字一段很长很长的内'), + TDCell( + arrow: true, + title: '多行带头像', + description: '一段很长很长的内容文字', + image: AssetImage('assets/img/td_avatar_1.png'), + ), + // NetworkImage('https://tdesign.gtimg.com/mobile/demos/avatar1.png')), + TDCell( + arrow: true, + title: '多行带图片', + description: '一段很长很长的内容文字', + image: AssetImage('assets/img/image.png'), + imageCircle: 8, + ), + ], + ); +} + +@Demo(group: 'cell') +Widget _buildCard(BuildContext context) { + return const TDCellGroup( + theme: TDCellGroupTheme.cardTheme, + cells: [ + TDCell(arrow: true, title: '单行标题'), + TDCell(arrow: true, title: '单行标题', required: true), + TDCell(arrow: true, title: '单行标题'), + ], + ); +} + +@Demo(group: 'cell') +Widget _buildPadding(BuildContext context) { + var style = TDCellStyle(context: context); + style.padding = const EdgeInsets.all(30); + return TDCellGroup( + theme: TDCellGroupTheme.cardTheme, + cells: [ + TDCell( + arrow: true, + title: 'padding-all-30', + style: style, + onClick: (cell) { + print('padding-all-30'); + }, + ), + ], + ); +} + +// @Demo(group: 'cell') +// Widget _buildBorder(BuildContext context) { +// return const TDCellGroup( +// theme: TDCellGroupTheme.cardTheme, +// bordered: true, +// cells: [ +// TDCell(arrow: true, title: '单行标题'), +// TDCell(arrow: true, title: '单行标题', required: true), +// TDCell(arrow: true, title: '单行标题'), +// ], +// ); +// } + +// @Demo(group: 'cell') +// Widget _buildTitle(BuildContext context) { +// var style = TDCellStyle.cellStyle(context); +// style.leftIconColor = TDTheme.of(context).fontGyColor1; +// return TDCellGroup( +// title: '标题', +// style: style, +// cells: const [ +// TDCell(title: 'item', leftIcon: TDIcons.app), +// TDCell(title: 'item', leftIcon: TDIcons.app), +// TDCell(title: 'item', leftIcon: TDIcons.app), +// ], +// ); +// } diff --git a/tdesign-component/example/lib/page/td_checkbox_page.dart b/tdesign-component/example/lib/page/td_checkbox_page.dart new file mode 100644 index 000000000..c88864880 --- /dev/null +++ b/tdesign-component/example/lib/page/td_checkbox_page.dart @@ -0,0 +1,474 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +/// +/// TDCheckbox演示 +/// +class TDCheckboxPage extends StatefulWidget { + const TDCheckboxPage({Key? key}) : super(key: key); + + @override + State createState() { + return TDCheckboxPageState(); + } +} + +class TDCheckboxPageState extends State { + + List? checkIds = ['index:1','index:2','index:3',]; + + TDCheckboxGroupController? controller; + + @override + void initState() { + super.initState(); + controller = TDCheckboxGroupController(); + } + + @override + Widget build(BuildContext context) { + + return ExamplePage( + title: tdTitle(), + desc: '用于预设的一组选项中执行多项选择,并呈现选择结果。', + exampleCodeGroup: 'checkbox', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '纵向多选框', builder: _verticalCheckbox), + ExampleItem(desc: '横向多选框', builder: _horizontalCheckbox), + ExampleItem(desc: '带全选多选框', builder: _checkAllSelected) + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '多选框状态', builder: _checkboxStatus), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem(desc: '勾选样式', builder: _checkStyle), + ExampleItem(desc: '勾选显示位置', builder: _checkPosition), + ExampleItem(desc: '非通栏多选样式', builder: _passThroughStyle), + ]), + ExampleModule(title: '特殊样式', children: [ + ExampleItem(desc: '纵向卡片单选框', builder: _verticalCardStyle), + ExampleItem(desc: '横向卡片单选框', builder: _horizontalCardStyle), + ]), + ], + test: [ + ExampleItem(desc: '自定义Icon', builder: _customIconBuildStyle), + ExampleItem(desc: '自定义颜色', builder: _customColor), + ExampleItem(desc: '自定义字体尺寸', builder: _customFont), + ],); + } + + @Demo(group: 'checkbox') + Widget _verticalCheckbox(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['index:1'], + child: ListView.builder( + padding: EdgeInsets.zero, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + var title = '多选'; + var subTitle = ''; + if (index == 2) { + title = '多选标题多行多选标题多行多选标题多行多选标题多行多选标题多行多选标题多行'; + } + if (index == 3) { + subTitle = '描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息'; + } + return TDCheckbox( + id: 'index:$index', + title: title, + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: subTitle, + ); + }, + itemCount: 4, + ), + ); + } + + @Demo(group: 'checkbox') + Widget _horizontalCheckbox(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['1'], + direction: Axis.horizontal, + directionalTdCheckboxes: const [ + TDCheckbox( + id: '0', + title: '多选标题', + style: TDCheckboxStyle.circle, + insetSpacing: 12, + showDivider: false, + ), + TDCheckbox( + id: '1', + title: '多选标题', + style: TDCheckboxStyle.circle, + insetSpacing: 12, + showDivider: false, + ), + TDCheckbox( + id: '2', + title: '上限四字', + style: TDCheckboxStyle.circle, + insetSpacing: 12, + showDivider: false, + ), + ], + ); + } + + @Demo(group: 'checkbox') + Widget _checkAllSelected(BuildContext context) { + const itemCount = 4; + return TDCheckboxGroupContainer( + selectIds: checkIds, + passThrough: false, + controller: controller, + child: ListView.builder( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + var title = '多选'; + if(index == 0){ + title = '全选'; + return SizedBox( + height: 56, + child: TDCheckbox( + id: 'index:$index', + title: title, + customIconBuilder: (context, checked) { + var length = controller!.allChecked().length - (controller!.checked('index:0') ? 1 : 0); + var allCheck = itemCount - 1 == length; + var halfSelected = + controller != null + && !allCheck + && length > 0; + return getAllIcon(allCheck, halfSelected); + }, + onCheckBoxChanged: (checked){ + if (checked) { + controller?.toggleAll(true); + } else { + controller?.toggleAll(false); + } + }, + ), + ); + }else{ + return SizedBox( + height: index == itemCount - 1 ? null : 56, + child: TDCheckbox( + id: 'index:$index', + title: title, + subTitle: index == itemCount - 1 ? '描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息' : null, + subTitleMaxLine: 2, + onCheckBoxChanged: (checked){ + var length = controller!.allChecked().length - (controller!.checked('index:0') ? 1 : 0); + var allCheck = itemCount - 1 == length; + var halfSelected = + controller != null + && !allCheck + && length > 0; + controller!.toggle('index:0', allCheck); + getAllIcon(allCheck, halfSelected); + }, + ), + ); + } + }, + itemCount: itemCount, + ), + ); + } + + @Demo(group: 'checkbox') + Widget _checkboxStatus(BuildContext context) { + return TDCheckboxGroupContainer( + contentDirection: TDContentDirection.right, + selectIds: const ['0'], + child: Column( + children: const [ + TDCheckbox( + id: '0', + title: '选项禁用-已选', + style: TDCheckboxStyle.circle, + enable: false, + ), + TDCheckbox( + id: '1', + title: '选项禁用-默认', + style: TDCheckboxStyle.circle, + enable: false, + ), + ], + ), + ); + } + + @Demo(group: 'checkbox') + Widget _checkStyle(BuildContext context) { + return Column( + children: [ + TDCheckboxGroupContainer( + style: TDCheckboxStyle.check, + selectIds: const ['index:0'], + child: const TDCheckbox( + id: 'index:0', + title: '多选', + ), + ), + const SizedBox( + height: 17, + ), + TDCheckboxGroupContainer( + style: TDCheckboxStyle.square, + selectIds: const ['index:0'], + child: const TDCheckbox( + id: 'index:0', + title: '多选', + ), + ) + ], + ); + } + + @Demo(group: 'checkbox') + Widget _checkPosition(BuildContext context) { + return Column( + children: [ + TDCheckboxGroupContainer( + contentDirection: TDContentDirection.right, + selectIds: const ['index:0'], + child: const TDCheckbox( + id: 'index:0', + title: '多选', + ), + ), + TDCheckboxGroupContainer( + contentDirection: TDContentDirection.left, + selectIds: const ['index:0'], + child: const TDCheckbox( + id: 'index:0', + title: '多选', + ), + ) + ], + ); + } + + @Demo(group: 'checkbox') + Widget _passThroughStyle(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['index:0'], + passThrough: true, + child: ListView.builder( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + var title = '多选'; + return TDCheckbox( + id: 'index:$index', + title: title, + size: TDCheckBoxSize.large, + ); + }, + itemCount: 4, + ), + ); + } + + @Demo(group: 'checkbox') + Widget _verticalCardStyle(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['index:1'], + cardMode: true, + direction: Axis.vertical, + directionalTdCheckboxes: const [ + TDCheckbox( + id: 'index:0', + title: '多选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDCheckbox( + id: 'index:1', + title: '多选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDCheckbox( + id: 'index:2', + title: '多选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDCheckbox( + id: 'index:3', + title: '多选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + ], + ); + } + + @Demo(group: 'checkbox') + Widget _horizontalCardStyle(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['index:1'], + cardMode: true, + direction: Axis.horizontal, + directionalTdCheckboxes: const [ + TDCheckbox( + id: 'index:0', + title: '多选', + cardMode: true, + ), + TDCheckbox( + id: 'index:1', + title: '多选', + cardMode: true, + ), + TDCheckbox( + id: 'index:2', + title: '多选', + cardMode: true, + ), + ], + ); + } + + @Demo(group: 'checkbox') + Widget _customIconBuildStyle(BuildContext context) { + return TDCheckboxGroupContainer( + selectIds: const ['index:1'], + cardMode: true, + direction: Axis.vertical, + directionalTdCheckboxes: [ + TDCheckbox( + id: 'index:0', + title: '多选', + subTitle: '描述信息', + titleMaxLine: 2, + subTitleMaxLine: 2, + cardMode: true, + customIconBuilder: (context, checked){ + return const Icon(TDIcons.app, size: 12,); + }, + ), + ], + ); + } + @Demo(group: 'checkbox') + Widget _customColor(BuildContext context) { + return TDCheckboxGroupContainer( + contentDirection: TDContentDirection.right, + selectIds: const ['0'], + child: Column( + children: [ + TDCheckbox( + selectColor: TDTheme.of(context).errorColor3, + disableColor: TDTheme.of(context).errorColor1, + id: '0', + title: '选项禁用-已选', + style: TDCheckboxStyle.circle, + enable: false, + ), + TDCheckbox( + selectColor: TDTheme.of(context).errorColor3, + disableColor: TDTheme.of(context).errorColor1, + id: '1', + title: '选项禁用-默认', + style: TDCheckboxStyle.circle, + ), + + TDCheckbox( + selectColor: TDTheme.of(context).errorColor3, + disableColor: TDTheme.of(context).errorColor1, + id: 'index:0', + title: '多选', + subTitle: '描述信息', + titleMaxLine: 2, + subTitleMaxLine: 2, + cardMode: true, + ), + + TDCheckbox( + selectColor: TDTheme.of(context).errorColor3, + id: 'index:1', + title: '多选', + titleColor: Colors.green, + subTitle: '描述信息', + subTitleColor: Colors.blue, + titleMaxLine: 2, + subTitleMaxLine: 2, + cardMode: true, + ), + ], + ), + ); + } + + @Demo(group: 'checkbox') + Widget _customFont(BuildContext context) { + return TDCheckboxGroupContainer( + contentDirection: TDContentDirection.right, + selectIds: const ['0'], + child: Column( + children: [ + TDCheckbox( + id: '0', + title: '选项禁用-已选', + subTitle: '描述文本', + style: TDCheckboxStyle.circle, + enable: false, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + TDCheckbox( + id: '1', + title: '选项禁用-默认', + subTitle: '描述文本', + style: TDCheckboxStyle.circle, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + + TDCheckbox( + id: 'index:0', + title: '多选', + subTitle: '描述信息', + titleMaxLine: 2, + subTitleMaxLine: 2, + cardMode: true, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + ], + ), + ); + } + + Widget getAllIcon(bool checked, bool halfSelected) { + return Icon( + checked ? TDIcons.check_circle_filled : halfSelected ? TDIcons.minus_circle_filled : TDIcons.circle, + size: 24, + color: (checked || halfSelected) ? TDTheme.of(context).brandNormalColor : TDTheme.of(context).grayColor4 + ); + } +} diff --git a/tdesign-component/example/lib/page/td_collapse_page.dart b/tdesign-component/example/lib/page/td_collapse_page.dart new file mode 100644 index 000000000..7e7b5ed69 --- /dev/null +++ b/tdesign-component/example/lib/page/td_collapse_page.dart @@ -0,0 +1,185 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; +class TDCollapsePage extends StatefulWidget { + const TDCollapsePage({Key? key}) : super(key: key); + + @override + TDCollapsePageState createState() => TDCollapsePageState(); +} + +const String randomString = + "In the heart of the bustling city, a small park offered a sanctuary of tranquility.Children's laughter echoed from the playground, mingling with the soft rustling of leaves in the gentle breeze.Joggers navigated winding paths, their steady breaths in rhythm with the chirping of the early morning birds.Nearby, an elderly man sat on a bench, engrossed in a book, oblivious to the world around him.The park was a microcosm of life, a testament to the city's vibrant spirit and the enduring allure of nature's simple pleasures."; + +class TDCollapsePageState extends State { + final List _basicData = generateItems(5); + final List _blockStyleData = generateItems(5); + final List _cardStyleData = generateItems(5); + final List _blockStyleWithOpText = generateItems(5); + final List _accordionData = generateItems(5); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + exampleCodeGroup: 'collapse', + desc: '可以折叠/展开的内容区域。', + children: [ + ExampleModule(title: 'Type 组件类型', children: [ + ExampleItem( + desc: 'Basic 基础折叠面板', + builder: _buildBasicCollapse, + ), + ExampleItem( + desc: 'with Operation Instructions 带操作说明', + builder: _buildCollapseWithOperationText, + ), + ExampleItem( + desc: 'Accordion 手风琴式', + builder: _buildAccordionCollapse, + ), + ]), + ExampleModule(title: 'Style 组件样式', children: [ + ExampleItem( + desc: 'Block Style 通栏样式', + builder: _buildBlockStyleCollapse, + ), + ExampleItem( + desc: 'Card Style 卡片样式', + builder: _buildCardCollapse, + ), + ]), + ]); + } + + @Demo(group: 'collapse') + Widget _buildBasicCollapse(BuildContext context) { + return TDCollapse( + style: TDCollapseStyle.block, + expansionCallback: (int index, bool isExpanded) { + setState(() { + _basicData[index].isExpanded = !isExpanded; + }); + }, + children: _basicData.map((CollapseDataItem item) { + return TDCollapsePanel( + headerBuilder: (BuildContext context, bool isExpanded) { + return Text(item.headerValue); + }, + isExpanded: item.isExpanded, + body: const Text(randomString), + ); + }).toList(), + ); + } + + @Demo(group: 'collapse') + Widget _buildBlockStyleCollapse(BuildContext context) { + return TDCollapse( + style: TDCollapseStyle.block, + expansionCallback: (int index, bool isExpanded) { + setState(() { + _blockStyleData[index].isExpanded = !isExpanded; + }); + }, + children: _blockStyleData.map((CollapseDataItem item) { + return TDCollapsePanel( + headerBuilder: (BuildContext context, bool isExpanded) { + return Text(item.headerValue); + }, + isExpanded: item.isExpanded, + body: const Text(randomString), + ); + }).toList(), + ); + } + + @Demo(group: 'collapse') + Widget _buildCardCollapse(BuildContext context) { + return TDCollapse( + style: TDCollapseStyle.card, + expansionCallback: (int index, bool isExpanded) { + setState(() { + _cardStyleData[index].isExpanded = !isExpanded; + }); + }, + children: _cardStyleData.map((CollapseDataItem item) { + return TDCollapsePanel( + headerBuilder: (BuildContext context, bool isExpanded) { + return Text(item.headerValue); + }, + isExpanded: item.isExpanded, + body: const Text(randomString), + ); + }).toList(), + ); + } + + @Demo(group: 'collapse') + Widget _buildCollapseWithOperationText(BuildContext context) { + return TDCollapse( + style: TDCollapseStyle.block, + expansionCallback: (int index, bool isExpanded) { + setState(() { + _blockStyleWithOpText[index].isExpanded = !isExpanded; + }); + }, + children: _blockStyleWithOpText.map((CollapseDataItem item) { + return TDCollapsePanel( + headerBuilder: (BuildContext context, bool isExpanded) { + return Text(item.headerValue); + }, + expandIconTextBuilder: (BuildContext context, bool isExpanded) { + return isExpanded ? '收起' : '展开'; + }, + isExpanded: item.isExpanded, + body: const Text(randomString), + ); + }).toList(), + ); + } + + @Demo(group: 'collapse') + Widget _buildAccordionCollapse(BuildContext context) { + return TDCollapse.accordion( + style: TDCollapseStyle.block, + expansionCallback: (int index, bool isExpanded) { + setState(() { + _accordionData[index].isExpanded = !isExpanded; + }); + }, + children: _accordionData.map((CollapseDataItem item) { + return TDCollapsePanel( + headerBuilder: (BuildContext context, bool isExpanded) { + return Text(item.headerValue); + }, + isExpanded: item.isExpanded, + body: const Text(randomString), + value: item.expandedValue, + ); + }).toList(), + ); + } +} + +class CollapseDataItem { + CollapseDataItem( + {required this.expandedValue, + required this.headerValue, + this.isExpanded = false}); + + final String expandedValue; + final String headerValue; + bool isExpanded; +} + +List generateItems(int numOfItems) { + return List.generate(numOfItems, (index) { + return CollapseDataItem( + headerValue: '标题 $index', + expandedValue: '$index', + ); + }); +} diff --git a/tdesign-component/example/lib/page/td_date_picker_page.dart b/tdesign-component/example/lib/page/td_date_picker_page.dart new file mode 100644 index 000000000..e17d582f5 --- /dev/null +++ b/tdesign-component/example/lib/page/td_date_picker_page.dart @@ -0,0 +1,456 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDDatePickerPage extends StatefulWidget { + const TDDatePickerPage({Key? key}) : super(key: key); + + @override + State createState() => _TDDatePickerPageState(); +} + +class _TDDatePickerPageState extends State { + String selected_1 = ''; + String selected_2 = ''; + String selected_3 = ''; + String selected_4 = ''; + String selected_5 = ''; + String selected_6 = ''; + String selected_7 = ''; + String selected_8 = ''; + String selected_9 = ''; + + var weekDayList = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '用于选择一个时间点或者一个时间段。', + exampleCodeGroup: 'datetimePicker', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '年月日选择器', builder: buildYearMonthDay), + ExampleItem(desc: '年月选择器', builder: buildYearMonth), + ExampleItem(desc: '月日选择器', builder: buildMonthDay), + ExampleItem(desc: '时分秒选择器', builder: buildHourMinuteSecond), + ExampleItem(desc: '年月日时分秒选择器', builder: buildAll), + ExampleItem(desc: '年月日带星期选择器', builder: buildWeekDay), + ], + ), + ExampleModule( + title: '组件样式', + children: [ + ExampleItem(desc: '是否带标题', builder: buildWithTitle), + ExampleItem(desc: '不带标题', builder: buildWithoutTitle), + ], + ) + ], + test: [ + ExampleItem(desc: '指定开始时间', builder: _customStartTime), + ExampleItem(desc: '限制时分秒时间', builder: _customLimitTime), + ExampleItem(desc: '自定义时间选项', builder: _customItems), + ExampleItem(desc: '自定义选中选项', builder: _customSelectWidget), + ExampleItem(desc: '只有时分限制时间', builder: _customItemsOnlyHour), + ], + ); + } + + @Demo(group: 'datetimePicker') + Widget buildYearMonthDay(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_1 = + '${selected['year'].toString().padLeft(4, '0')}-${selected['month'].toString().padLeft(2, '0')}-${selected['day'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_1, '选择时间'), + ); + } + + @Demo(group: 'datetimePicker') + Widget buildYearMonth(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_2 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useDay: false, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_2, '选择时间'), + ); + } + + @Demo(group: 'datetimePicker') + Widget buildMonthDay(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_3 = '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useYear: false, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_3, '选择时间'), + ); + } + + @Demo(group: 'datetimePicker') + Widget buildHourMinuteSecond(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_4 = '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useYear: false, + useMonth: false, + useDay: false, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31, 4, 12, 20], + initialDate: [2023, 12, 31]); + }, + child: buildSelectRow(context, selected_4, '选择时间'), + ); + } + + @Demo(group: 'datetimePicker') + Widget buildAll(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_5 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')} ' + '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_5, '选择时间'), + ); + } + + @Demo(group: 'datetimePicker') + Widget buildWeekDay(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_6 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')} ' + '${weekDayList[selected['weekDay']! - 1]}'; + }); + Navigator.of(context).pop(); + }, + useWeekDay: true, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_6, '选择时间'), + ); + } + + @Demo(group: 'datetimePicker') + Widget buildWithTitle(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_7 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_7, '带标题时间选择器'), + ); + } + + @Demo(group: 'datetimePicker') + Widget buildWithoutTitle(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '', onConfirm: (selected) { + setState(() { + selected_8 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1]); + }, + child: buildSelectRow(context, selected_8, '无标题时间选择器'), + ); + } + + Widget buildSelectRow(BuildContext context, String output, String title) { + return Container( + color: TDTheme.of(context).whiteColor1, + height: 56, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, top: 16, bottom: 16), + child: TDText( + title, + font: TDTheme.of(context).fontBodyLarge, + ), + ), + Padding( + padding: const EdgeInsets.only(right: 16), + child: Row( + children: [ + TDText( + output, + font: TDTheme.of(context).fontBodyLarge, + textColor: + TDTheme.of(context).fontGyColor3.withOpacity(0.4), + ), + Padding( + padding: const EdgeInsets.only(left: 2), + child: Icon( + TDIcons.chevron_right, + color: + TDTheme.of(context).fontGyColor3.withOpacity(0.4), + ), + ), + ], + ), + ), + ], + ), + const TDDivider( + margin: EdgeInsets.only( + left: 16, + ), + ) + ], + ), + ); + } + + @Demo(group: 'datetimePicker') + Widget _customStartTime(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_5 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')} ' + '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useYear: true, + useMonth: true, + useDay: true, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [2012, 1, 15, 12, 28, 11], + dateEnd: [2012, 6, 15, 12, 48, 32], + initialDate: [2012, 1, 15, 13, 20]); + }, + child: buildSelectRow(context, selected_5, '选择时间'), + ); + } + + @Demo(group: 'datetimePicker') + Widget _customLimitTime(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_4 = '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useYear: false, + useMonth: false, + useDay: false, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [2023, 12, 31], + dateEnd: [2023, 12, 31, 4, 12, 20], + initialDate: [2023, 12, 31, 3, 02, 03]); + }, + child: buildSelectRow(context, selected_4, '选择时间'), + ); + } + + @Demo(group: 'datetimePicker') + Widget _customItems(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker( + context, + title: '选择时间', + onConfirm: (selected) { + setState(() { + selected_9 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')} ' + '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1], + filterItems: (key, nums) { + if (key == DateTypeKey.minute) { + return [0, 15, 30]; + } + return nums; + }, + itemBuilder: (context, content, colIndex, index, + itemDistanceCalculator, distance) { + return colIndex == 5 + ? TDText( + content, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: itemDistanceCalculator.calculateFontWeight( + context, distance), + fontSize: index % 2 == 0 ? 20 : 10, + color: index % 2 == 1 + ? TDTheme.of(context).fontGyColor1 + : TDTheme.of(context).successColor6, + ), + ) + : null; + }, + ); + }, + child: buildSelectRow(context, selected_9, '选择时间'), + ); + } + + @Demo(group: 'datetimePicker') + Widget _customItemsOnlyHour(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker( + context, + title: '只有时分', + onConfirm: (selected) { + Navigator.of(context).pop(); + }, + useYear: false, + useMonth: false, + useDay: false, + useSecond: false, + useHour: true, + useMinute: true, + dateStart: [2025, 1, 1, 20, 0, 0], + dateEnd: [2025, 1, 1, 23, 59, 0], + initialDate: [2025, 1, 1, 22, 46, 0], + ); + }, + child: buildSelectRow(context, selected_9, '选择时间'), + ); + } + + @Demo(group: 'datetimePicker') + Widget _customSelectWidget(BuildContext context) { + return GestureDetector( + onTap: () { + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + selected_9 = '${selected['year'].toString().padLeft(4, '0')}-' + '${selected['month'].toString().padLeft(2, '0')}-' + '${selected['day'].toString().padLeft(2, '0')} ' + '${selected['hour'].toString().padLeft(2, '0')}:' + '${selected['minute'].toString().padLeft(2, '0')}:' + '${selected['second'].toString().padLeft(2, '0')}'; + }); + Navigator.of(context).pop(); + }, + useHour: true, + useMinute: true, + useSecond: true, + dateStart: [1999, 01, 01], + dateEnd: [2023, 12, 31], + initialDate: [2012, 1, 1], + customSelectWidget: Container( + height: 40, + decoration: const BoxDecoration( + color: Colors.red, + borderRadius: BorderRadius.all(Radius.circular(6))), + )); + }, + child: buildSelectRow(context, selected_9, '选择时间'), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_dialog_page.dart b/tdesign-component/example/lib/page/td_dialog_page.dart new file mode 100644 index 000000000..a3ee1267c --- /dev/null +++ b/tdesign-component/example/lib/page/td_dialog_page.dart @@ -0,0 +1,771 @@ +/* + * Created by haozhicao@tencent.com on 6/17/22. + * td_dialog_page.dart + * + */ + +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDDialogPage extends StatefulWidget { + const TDDialogPage({Key? key}) : super(key: key); + + @override + State createState() => _TDDialogPageState(); +} + +class _TDDialogPageState extends State { + final _dialogTitle = '对话框标题'; + final _commonContent = '告知当前状态、信息和解决方法,等内容。描述尽可能控制在三行内。'; + final _longContent = '这里是辅助内容文案,这里是辅助内容文案,这里是辅助内容文案,这里是辅助内容文案。\n\n' * 4; + final _inputHint = '请输入文字'; + + final _demoImage = Image.asset( + 'assets/img/image.png', + ); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '用于显示重要提示或请求用户进行重要操作,一种打断当前操作的模态视图。', + exampleCodeGroup: 'dialog', + padding: const EdgeInsets.only(top: 8, bottom: 8), + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '反馈类对话框', builder: _buildFeedbackNormal), + ExampleItem(builder: _buildFeedbackNoTitle), + ExampleItem(builder: _buildFeedbackOnlyTitle), + ExampleItem(builder: _buildFeedbackLongContent), + ExampleItem(desc: '确认类对话框', builder: _buildConfirmNormal), + ExampleItem(builder: _buildConfirmNoTitle), + ExampleItem(builder: _buildConfirmOnlyTitle), + ExampleItem(desc: '输入类对话框', builder: _buildInputNormal), + ExampleItem(builder: _buildInputNoContent), + ExampleItem(desc: '带图片的对话框', builder: _buildImageTop), + ExampleItem(builder: _buildImageTopNoTitle), + ExampleItem(builder: _buildImageTopOnlyTitle), + ExampleItem(builder: _buildImageMiddle), + ExampleItem(builder: _buildImageMiddleOnlyTitle), + ExampleItem(builder: _buildImageMiddleOnlyImage), + ]), + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '文字按钮', builder: _buildTextButtonSingle), + ExampleItem(builder: _buildTextButtonDouble), + ExampleItem(desc: '横向基础按钮', builder: _buildNormalButtonSingle), + ExampleItem(builder: _buildNormalButtonDouble), + ExampleItem(desc: '纵向基础按钮', builder: _buildVerticalButtonDouble), + ExampleItem(builder: _buildVerticalButtonTriple), + ExampleItem( + desc: '带关闭按钮的对话框', builder: _buildDialogWithCloseButton), + ]), + ], + test: [ + ExampleItem(desc: '自定义标题对齐和内容组件', builder: _customFeedbackNormal), + ExampleItem(builder: _customConfirmNormal), + ExampleItem(builder: _customConfirmVertical), + ExampleItem(builder: _customImageTop), + ExampleItem(desc: '自定义边距和按钮', builder: _customContentAndBtn) + ],); + } + + // 反馈类 + @Demo(group: 'dialog') + Widget _buildFeedbackNormal(BuildContext context) { + return TDButton( + text: '反馈类-带标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _commonContent, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildFeedbackNoTitle(BuildContext context) { + return TDButton( + text: '反馈类-无标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + content: _commonContent, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildFeedbackOnlyTitle(BuildContext context) { + return TDButton( + text: '反馈类-纯标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildFeedbackLongContent(BuildContext context) { + return TDButton( + text: '反馈类-内容超长', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _longContent, + contentMaxHeight: 300, + ); + }, + ); + }, + ); + } + + // 确认类 + @Demo(group: 'dialog') + Widget _buildConfirmNormal(BuildContext context) { + return TDButton( + text: '确认类-带标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + title: _dialogTitle, + content: _commonContent, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildConfirmNoTitle(BuildContext context) { + return TDButton( + text: '确认类-无标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + content: _commonContent, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildConfirmOnlyTitle(BuildContext context) { + return TDButton( + text: '确认类-纯标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + title: _dialogTitle, + ); + }, + ); + }, + ); + } + + // 输入类 + @Demo(group: 'dialog') + Widget _buildInputNormal(BuildContext context) { + return TDButton( + text: '输入类-带描述', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDInputDialog( + textEditingController: TextEditingController(), + title: _dialogTitle, + content: _commonContent, + hintText: _inputHint, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildInputNoContent(BuildContext context) { + return TDButton( + text: '输入类-无描述', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDInputDialog( + textEditingController: TextEditingController(), + title: _dialogTitle, + hintText: _inputHint, + ); + }, + ); + }, + ); + } + + // 图片类型 + @Demo(group: 'dialog') + Widget _buildImageTop(BuildContext context) { + return TDButton( + text: '图片置顶-带标题描述', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + title: _dialogTitle, + content: _commonContent, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildImageTopNoTitle(BuildContext context) { + return TDButton( + text: '图片置顶-无标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + content: _commonContent, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildImageTopOnlyTitle(BuildContext context) { + return TDButton( + text: '图片置顶-纯标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + title: _dialogTitle, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildImageMiddle(BuildContext context) { + return TDButton( + text: '图片居中-带标题描述', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + title: _dialogTitle, + content: _commonContent, + imagePosition: TDDialogImagePosition.middle, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildImageMiddleOnlyTitle(BuildContext context) { + return TDButton( + text: '图片居中-纯标题', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + title: _dialogTitle, + imagePosition: TDDialogImagePosition.middle, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildImageMiddleOnlyImage(BuildContext context) { + return TDButton( + text: '图片居中-纯图片', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + imagePosition: TDDialogImagePosition.middle, + ); + }, + ); + }, + ); + } + + // 文字按钮 + @Demo(group: 'dialog') + Widget _buildTextButtonSingle(BuildContext context) { + return TDButton( + text: '单个文字按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _commonContent, + buttonStyle: TDDialogButtonStyle.text, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildTextButtonDouble(BuildContext context) { + return TDButton( + text: '左右文字按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + title: _dialogTitle, + content: _commonContent, + buttonStyle: TDDialogButtonStyle.text, + ); + }, + ); + }, + ); + } + + // 横向基础按钮 + @Demo(group: 'dialog') + Widget _buildNormalButtonSingle(BuildContext context) { + return TDButton( + text: '单个横向基础按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _commonContent, + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildNormalButtonDouble(BuildContext context) { + return TDButton( + text: '左右横向基础按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + title: _dialogTitle, + content: _commonContent, + ); + }, + ); + }, + ); + } + + // 纵向基础按钮 + @Demo(group: 'dialog') + Widget _buildVerticalButtonDouble(BuildContext context) { + return TDButton( + text: '两个纵向基础按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog.vertical( + title: _dialogTitle, + content: _commonContent, + buttons: [ + TDDialogButtonOptions( + title: '主要按钮', + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.primary), + TDDialogButtonOptions( + title: '次要按钮', + titleColor: TDTheme.of(context).brandColor7, + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.light), + ]); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildVerticalButtonTriple(BuildContext context) { + return TDButton( + text: '三个纵向基础按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog.vertical( + title: _dialogTitle, + content: _commonContent, + buttons: [ + TDDialogButtonOptions( + title: '主要按钮', + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.primary), + TDDialogButtonOptions( + title: '次要按钮', + titleColor: TDTheme.of(context).brandColor7, + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.light), + TDDialogButtonOptions( + title: '次要按钮', + titleColor: TDTheme.of(context).brandColor7, + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.light), + ]); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _buildDialogWithCloseButton(BuildContext context) { + return TDButton( + text: '带关闭按钮的对话框', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _commonContent, + showCloseButton: true, + ); + }, + ); + }, + ); + } + + // 反馈类 + @Demo(group: 'dialog') + Widget _customFeedbackNormal(BuildContext context) { + return TDButton( + text: '反馈类-标题偏左', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + titleAlignment: Alignment.centerLeft, + contentWidget: TDText.rich( + TDTextSpan( + children: [ + TDTextSpan(text: '红色文字', textColor: Colors.red), + TDTextSpan(text: '绿色文字', textColor: Colors.green), + ] + ) + ), + ); + }, + ); + }, + ); + } + @Demo(group: 'dialog') + Widget _customConfirmNormal(BuildContext context) { + return TDButton( + text: '确认类-标题偏右', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog( + title: _dialogTitle, + titleAlignment: Alignment.centerRight, + contentWidget: TDText.rich( + TDTextSpan( + children: [ + TDTextSpan(text: '红色文字', textColor: Colors.red), + TDTextSpan(text: '绿色文字', textColor: Colors.green), + ] + ) + ), + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _customConfirmVertical(BuildContext context) { + return TDButton( + text: '纵向按钮-自定义内容', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDAlertDialog.vertical( + title: _dialogTitle, + contentWidget: TDText.rich( + TDTextSpan( + children: [ + TDTextSpan(text: '红色文字', textColor: Colors.red), + TDTextSpan(text: '绿色文字', textColor: Colors.green), + ] + ) + ), + buttons: [ + TDDialogButtonOptions( + title: '主要按钮', + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.primary), + TDDialogButtonOptions( + title: '次要按钮', + titleColor: TDTheme.of(context).brandColor7, + action: () { + Navigator.pop(context); + }, + theme: TDButtonTheme.light), + ]); + }, + ); + }, + ); + } + @Demo(group: 'dialog') + Widget _customImageTop(BuildContext context) { + return TDButton( + text: '图片置顶-自定义列表内容', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDImageDialog( + image: _demoImage, + title: _dialogTitle, + contentWidget: ListView( + shrinkWrap: true, + children: const [ + TDText('红色文字', textColor: Colors.red), + TDText('绿色文字', textColor: Colors.green), + ], + ), + ); + }, + ); + }, + ); + } + + @Demo(group: 'dialog') + Widget _customContentAndBtn(BuildContext context) { + return TDButton( + text: '自定义边距和按钮', + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + onTap: () { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + title: _dialogTitle, + content: _commonContent, + padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), + buttonWidget: Container( + padding: const EdgeInsets.fromLTRB(0, 16, 0, 16), + child: TDButton( + text: '自定义按钮', + theme: TDButtonTheme.primary, + onTap: () { + Navigator.of(context).pop(); + }, + ), + ), + ); + } + ); + } + ); + } +} diff --git a/tdesign-component/example/lib/page/td_divider_page.dart b/tdesign-component/example/lib/page/td_divider_page.dart new file mode 100644 index 000000000..311dc47b1 --- /dev/null +++ b/tdesign-component/example/lib/page/td_divider_page.dart @@ -0,0 +1,139 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDDividerPage extends StatelessWidget { + const TDDividerPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(context), + desc: '用于分割、组织、细化有一定逻辑的组织元素内容和页面结构。', + exampleCodeGroup: 'divider', + // padding: const EdgeInsets.only(top: 16, bottom: 16), + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '水平分割线', builder: _verticalDivider), + ExampleItem(desc: '带文字水平分割线', builder: _verticalTextDivider), + ExampleItem(desc: '垂直分割', builder: _horizontalTextDivider), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '虚线样式', builder: _dashedDivider), + ]) + ]); + } + + @Demo(group: 'divider') + Widget _verticalDivider(BuildContext context) { + return SizedBox( + height: 20, + child: Container( + alignment: Alignment.center, + child: const TDDivider(), + ), + ); + } + + @Demo(group: 'divider') + Widget _verticalTextDivider(BuildContext context) { + return Column( + children: const [ + TDDivider( + text: '文字信息', + alignment: TextAlignment.left, + ), + SizedBox( + height: 20, + ), + TDDivider( + text: '文字信息', + alignment: TextAlignment.center, + ), + SizedBox( + height: 20, + ), + TDDivider( + text: '文字信息', + alignment: TextAlignment.right, + ), + ], + ); + } + + @Demo(group: 'divider') + Widget _horizontalTextDivider(BuildContext context) { + return Container( + alignment: Alignment.centerLeft, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + const SizedBox( + width: 16, + ), + TDText( + '文字信息', + textColor: TDTheme.of(context).fontGyColor1.withOpacity(0.9), + ), + const TDDivider( + width: 0.5, + height: 12, + margin: EdgeInsets.only(left: 16, right: 16), + ), + TDText('文字信息', + textColor: TDTheme.of(context).fontGyColor1.withOpacity(0.9)), + const TDDivider( + width: 0.5, + height: 12, + margin: EdgeInsets.only(left: 16, right: 16), + isDashed: true, + direction: Axis.vertical, + ), + TDText('文字信息', + textColor: TDTheme.of(context).fontGyColor1.withOpacity(0.9)), + ], + ), + ); + } + + @Demo(group: 'divider') + Widget _dashedDivider(BuildContext context) { + return Column( + children: const [ + SizedBox( + height: 20, + ), + TDDivider( + isDashed: true, + ), + SizedBox( + height: 20, + ), + TDDivider( + text: '文字信息', + alignment: TextAlignment.left, + isDashed: true, + ), + SizedBox( + height: 20, + ), + TDDivider( + text: '文字信息', + alignment: TextAlignment.center, + isDashed: true, + ), + SizedBox( + height: 20, + ), + TDDivider( + text: '文字信息', + alignment: TextAlignment.right, + isDashed: true, + ), + ], + ); + } +} diff --git a/tdesign-component/example/lib/page/td_drawer_page.dart b/tdesign-component/example/lib/page/td_drawer_page.dart new file mode 100644 index 000000000..bea327ba7 --- /dev/null +++ b/tdesign-component/example/lib/page/td_drawer_page.dart @@ -0,0 +1,216 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +const _nums = [ + '一', + '二', + '三', + '四', + '五', + '六', + '七', + '八', + '九', + '十', + '十一', + '十二', + '十三', + '十四', + '十五', + '十六', + '十七', + '十八', + '十九', + '二十', + '二一', + '二二', + '二三', + '二四', + '二五', + '二六', + '二七', + '二八', + '二九', + '三十', +]; + +class TDDrawerPage extends StatelessWidget { + const TDDrawerPage({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: TDTheme.of(context).grayColor2, + child: ExamplePage( + title: tdTitle(context), + desc: '用作一组平行关系页面/内容的切换器,相较于Tab,同屏可展示更多的选项数量。', + exampleCodeGroup: 'drawer', + navBarKey: navBarkey, + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + ignoreCode: true, + desc: '基础抽屉', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildBaseSimple); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '带图标抽屉', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildIconSimple); + }, + ), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem( + ignoreCode: true, + desc: '带标题抽屉', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildTitleSimple); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '带底部插槽样式', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildBottomSimple); + }, + ), + ]), + ], + test: [ + ExampleItem( + ignoreCode: true, + desc: '自定义背景色', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildColorSimple); + }, + ) + ], + )); + } +} + +@Demo(group: 'drawer') +Widget _buildBaseSimple(BuildContext context) { + /// 获取navBar尺寸 + var renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + return TDButton( + text: '基础抽屉', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDDrawer( + context, + visible: true, + drawerTop: renderBox?.size.height, + items: List.generate(30, (index) => TDDrawerItem(title: '菜单${_nums[index]}')).toList(), + onItemClick: (index, item) { + print('drawer item被点击,index:$index,title:${item.title}'); + }, + ); + }, + ); +} + +@Demo(group: 'drawer') +Widget _buildIconSimple(BuildContext context) { + /// 获取navBar尺寸 + var renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + return TDButton( + text: '带图标抽屉', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDDrawer( + context, + visible: true, + drawerTop: renderBox?.size.height, + items: List.generate(30, (index) => TDDrawerItem(title: '菜单${_nums[index]}', icon: const Icon(TDIcons.app))).toList(), + ); + }, + ); +} + +@Demo(group: 'drawer') +Widget _buildTitleSimple(BuildContext context) { + /// 获取navBar尺寸 + var renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + return TDButton( + text: '带图标抽屉', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDDrawer( + context, + visible: true, + drawerTop: renderBox?.size.height, + title: '标题', + placement: TDDrawerPlacement.left, + items: List.generate(10, (index) => TDDrawerItem(title: '菜单${_nums[index]}')).toList(), + ); + }, + ); +} + +@Demo(group: 'drawer') +Widget _buildBottomSimple(BuildContext context) { + /// 获取navBar尺寸 + var renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + return TDButton( + text: '带底部插槽样式', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDDrawer( + context, + visible: true, + drawerTop: renderBox?.size.height, + title: '标题', + placement: TDDrawerPlacement.left, + items: List.generate(10, (index) => TDDrawerItem(title: '菜单${_nums[index]}')).toList(), + footer: const TDButton( + text: '操作', + type: TDButtonType.outline, + width: double.infinity, + size: TDButtonSize.large, + ), + ); + }, + ); +} + +@Demo(group: 'drawer') +Widget _buildColorSimple(BuildContext context) { + var renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + return TDButton( + text: '自定义背景色', + isBlock: true, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + onTap: () { + TDDrawer( + context, + visible: true, + drawerTop: renderBox?.size.height, + title: '标题', + backgroundColor: TDTheme.of(context).grayColor1, + placement: TDDrawerPlacement.right, + items: List.generate(10, (index) => TDDrawerItem(title: '菜单${_nums[index]}')).toList(), + ); + }, + ); +} diff --git a/tdesign-component/example/lib/page/td_dropdown_menu_page.dart b/tdesign-component/example/lib/page/td_dropdown_menu_page.dart new file mode 100644 index 000000000..fcf4782c4 --- /dev/null +++ b/tdesign-component/example/lib/page/td_dropdown_menu_page.dart @@ -0,0 +1,485 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDDropdownMenuPage extends StatelessWidget { + const TDDropdownMenuPage({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: TDTheme.of(context).grayColor2, + child: ExamplePage( + title: tdTitle(context), + desc: '菜单呈现数个并列的选项类目,用于整个页面的内容筛选,由菜单面板和菜单选项组成。', + exampleCodeGroup: 'dropdownMenu', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + ignoreCode: true, + desc: '单选下拉菜单', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildDownSimple); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '分栏下拉菜单', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildDownChunk); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '向上展开', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildUp); + }, + ), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem( + ignoreCode: true, + desc: '禁用状态', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildDisabled); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '分组菜单', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildGroup); + }, + ), + ]), + ], + test: [ + ExampleItem( + ignoreCode: true, + desc: '自动弹出方向', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildHidden); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '最大高度限制', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildHeight); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '可横向滚动菜单', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildOverflow); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '可横向滚动菜单(自定义禁用、选中颜色)', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildCustomOverflow); + }, + ), + ], + )); + } +} + +@Demo(group: 'dropdownMenu') +TDDropdownMenu _buildDownSimple(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.down, + onMenuOpened: (value) { + print('打开第$value个菜单'); + }, + onMenuClosed: (value) { + print('关闭第$value个菜单'); + }, + items: [ + TDDropdownItem( + options: [ + TDDropdownItemOption(label: '全部产品', value: 'all', selected: true), + TDDropdownItemOption(label: '最新产品', value: 'new'), + TDDropdownItemOption(label: '最火产品', value: 'hot'), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + TDDropdownItem( + options: [ + TDDropdownItemOption(label: '默认排序', value: 'default', selected: true), + TDDropdownItemOption(label: '价格从高到低', value: 'price'), + ], + ), + ], + ); +} + +@Demo(group: 'dropdownMenu') +TDDropdownMenu _buildDownChunk(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.down, + items: [ + TDDropdownItem( + label: '单列多选', + multiple: true, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + TDDropdownItemOption(label: '选项3', value: '3'), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '禁用选项', value: '9', disabled: true), + ], + onChange: (value) { + print('选择:$value'); + }, + onConfirm: (value) { + print('确定选择:$value'); + }, + onReset: () { + print('清空选择'); + }, + ), + TDDropdownItem( + // label: '双列单选', + multiple: false, + optionsColumns: 2, + maxHeight: 300, + options: [ + TDDropdownItemOption(label: '双列单选1', value: '1'), + TDDropdownItemOption(label: '双列单选2', value: '2', selected: true), + TDDropdownItemOption(label: '双列单选3', value: '3'), + TDDropdownItemOption(label: '双列单选4', value: '4'), + TDDropdownItemOption(label: '双列单选5', value: '5'), + TDDropdownItemOption(label: '双列单选6', value: '6'), + TDDropdownItemOption(label: '双列单选7', value: '7'), + TDDropdownItemOption(label: '双列单选8', value: '8'), + TDDropdownItemOption(label: '禁用选项', value: '9', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + ], + ), + TDDropdownItem( + label: '双列多选', + multiple: true, + optionsColumns: 2, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2', selected: true), + TDDropdownItemOption(label: '选项3', value: '3'), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '禁用选项', value: '9', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + ], + ), + TDDropdownItem( + label: '三列多选', + multiple: true, + optionsColumns: 3, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2', selected: true), + TDDropdownItemOption(label: '选项3', value: '3', selected: true), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '选项9', value: '9'), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '11', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '12', disabled: true), + ], + ), + ], + ); +} + +@Demo(group: 'dropdownMenu') +TDDropdownMenu _buildUp(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.up, + onMenuOpened: (value) { + print('打开第$value个菜单'); + }, + onMenuClosed: (value) { + print('关闭第$value个菜单'); + }, + builder: (context) { + return [ + TDDropdownItem( + options: [ + TDDropdownItemOption(label: '全部产品', value: 'all', selected: true), + TDDropdownItemOption(label: '最新产品', value: 'new'), + TDDropdownItemOption(label: '最火产品', value: 'hot'), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + TDDropdownItem( + options: [ + TDDropdownItemOption(label: '默认排序', value: 'default', selected: true), + TDDropdownItemOption(label: '价格从高到低', value: 'price'), + ], + ), + ]; + }, + ); +} + +@Demo(group: 'dropdownMenu') +TDDropdownMenu _buildDisabled(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.down, + builder: (context) { + return [ + const TDDropdownItem( + disabled: true, + label: '禁用菜单', + ), + const TDDropdownItem( + disabled: true, + label: '禁用菜单', + ), + ]; + }, + ); +} + +@Demo(group: 'dropdownMenu') +TDDropdownMenu _buildGroup(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.up, + builder: (context) { + return [ + TDDropdownItem( + label: '分组菜单', + multiple: true, + optionsColumns: 3, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true, group: '类型'), + TDDropdownItemOption(label: '选项2', value: '2', group: '类型'), + TDDropdownItemOption(label: '选项3', value: '3', group: '类型'), + TDDropdownItemOption(label: '选项4', value: '4', group: '类型'), + TDDropdownItemOption(label: '选项5', value: '5', group: '角色'), + TDDropdownItemOption(label: '选项6', value: '6', group: '角色'), + TDDropdownItemOption(label: '选项7', value: '7', group: '角色'), + TDDropdownItemOption(label: '选项8', value: '8', group: '角色'), + TDDropdownItemOption(label: '禁用选项', value: '9', disabled: true, group: '角色'), + ], + onChange: (value) { + print('选择:$value'); + }, + onConfirm: (value) { + print('确定选择:$value'); + }, + ), + ]; + }, + ); +} + +@Demo(group: 'dropdownMenu') +TDDropdownMenu _buildHidden(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.auto, + arrowIcon: TDIcons.caret_up_small, + builder: (context) { + return [ + TDDropdownItem( + label: '分组菜单', + multiple: true, + optionsColumns: 3, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true, group: '类型'), + TDDropdownItemOption(label: '选项2', value: '2', group: '类型'), + TDDropdownItemOption(label: '选项3', value: '3', group: '类型'), + TDDropdownItemOption(label: '选项4', value: '4', group: '类型'), + TDDropdownItemOption(label: '选项5', value: '5', group: '角色'), + TDDropdownItemOption(label: '选项6', value: '6', group: '角色'), + TDDropdownItemOption(label: '选项7', value: '7', group: '角色'), + TDDropdownItemOption(label: '选项8', value: '8', group: '角色'), + TDDropdownItemOption(label: '禁用选项', value: '9', disabled: true, group: '角色'), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + ]; + }, + ); +} + +@Demo(group: 'dropdownMenu') +TDDropdownMenu _buildHeight(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.up, + onMenuOpened: (value) { + print('打开第$value个菜单'); + }, + onMenuClosed: (value) { + print('关闭第$value个菜单'); + }, + builder: (context) { + return [ + TDDropdownItem( + label: '最大高度限制', + multiple: true, + maxHeight: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2', selected: true), + TDDropdownItemOption(label: '选项3', value: '3', selected: true), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '选项9', value: '9'), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '11', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '12', disabled: true), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + TDDropdownItem( + maxHeight: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + TDDropdownItemOption(label: '选项3', value: '3'), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '选项9', value: '9'), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '11', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '12', disabled: true), + ], + ), + ]; + }, + ); +} + +@Demo(group: 'dropdownMenu') +TDDropdownMenu _buildOverflow(BuildContext context) { + return TDDropdownMenu( + isScrollable: true, + tabBarAlign: MainAxisAlignment.spaceAround, + direction: TDDropdownMenuDirection.up, + onMenuOpened: (value) { + print('打开第$value个菜单'); + }, + onMenuClosed: (value) { + print('关闭第$value个菜单'); + }, + builder: (context) { + return [ + TDDropdownItem( + label: '最大高度限制', + multiple: true, + maxHeight: 200, + tabBarWidth: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2', selected: true), + TDDropdownItemOption(label: '选项3', value: '3', selected: true), + TDDropdownItemOption(label: '选项4', value: '4'), + TDDropdownItemOption(label: '选项5', value: '5'), + TDDropdownItemOption(label: '选项6', value: '6'), + TDDropdownItemOption(label: '选项7', value: '7'), + TDDropdownItemOption(label: '选项8', value: '8'), + TDDropdownItemOption(label: '选项9', value: '9'), + TDDropdownItemOption(label: '禁用选项', value: '10', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '11', disabled: true), + TDDropdownItemOption(label: '禁用选项', value: '12', disabled: true), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + TDDropdownItem( + maxHeight: 200, + tabBarWidth: 200, + tabBarAlign: MainAxisAlignment.start, + options: [ + TDDropdownItemOption(label: '选项1选项1选项1选项1选项1选项1选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + ], + ), + TDDropdownItem( + maxHeight: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + ], + ), + TDDropdownItem( + maxHeight: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + ], + ), + TDDropdownItem( + maxHeight: 200, + options: [ + TDDropdownItemOption(label: '选项1', value: '1', selected: true), + TDDropdownItemOption(label: '选项2', value: '2'), + ], + ), + ]; + }, + ); +} + +@Demo(group: 'dropdownMenu') +TDDropdownMenu _buildCustomOverflow(BuildContext context) { + return TDDropdownMenu( + direction: TDDropdownMenuDirection.up, + onMenuOpened: (value) { + print('打开第$value个菜单'); + }, + onMenuClosed: (value) { + print('关闭第$value个菜单'); + }, + items: [ + TDDropdownItem( + options: [ + TDDropdownItemOption(label: '全部产品', value: 'all', selected: true, selectedColor: Colors.red), + TDDropdownItemOption(label: '最新产品', value: 'new', selectedColor: Colors.blue), + TDDropdownItemOption(label: '最火产品', value: 'hot', selectedColor: Colors.green), + ], + onChange: (value) { + print('选择:$value'); + }, + ), + TDDropdownItem( + multiple: true, + options: [ + TDDropdownItemOption(label: '默认排序', value: 'default', selected: true, selectedColor: Colors.red), + TDDropdownItemOption(label: '价格从高到低', value: 'price', selectedColor: Colors.green), + + ], + ), + ], + ); +} \ No newline at end of file diff --git a/tdesign-component/example/lib/page/td_empty_page.dart b/tdesign-component/example/lib/page/td_empty_page.dart new file mode 100644 index 000000000..bc040b2d8 --- /dev/null +++ b/tdesign-component/example/lib/page/td_empty_page.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDEmptyPage extends StatefulWidget { + const TDEmptyPage({Key? key}) : super(key: key); + + @override + State createState() => _TDEmptyPageState(); +} + +class _TDEmptyPageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage(title: tdTitle(), exampleCodeGroup: 'empty', desc: '用于空状态时的占位提示。', children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '图标空状态', builder: _iconEmpty), + ExampleItem(desc: '自定义图片空状态', builder: _imageEmpty), + ExampleItem(desc: '带操作空状态', builder: _operationEmpty), + ExampleItem(desc: '自定义带操作空状态', builder: _operationCustomEmpty), + ]), + ]); + } + + @Demo(group: 'empty') + Widget _iconEmpty(BuildContext context) { + return const TDEmpty( + type: TDEmptyType.plain, + emptyText: '描述文字', + ); + } + + @Demo(group: 'empty') + Widget _imageEmpty(BuildContext context) { + return TDEmpty( + type: TDEmptyType.plain, + emptyText: '描述文字', + image: Container( + width: 120, + height: 120, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault), + image: const DecorationImage(image: AssetImage('assets/img/empty.png'))), + ), + ); + } + + @Demo(group: 'empty') + Widget _operationEmpty(BuildContext context) { + return const TDEmpty( + type: TDEmptyType.operation, + operationText: '操作按钮', + emptyText: '描述文字', + ); + } + + @Demo(group: 'empty') + Widget _operationCustomEmpty(BuildContext context) { + return TDEmpty( + type: TDEmptyType.operation, + emptyText: '描述文字', + customOperationWidget: Padding( + padding: const EdgeInsets.only(top: 32), + child: TDButton( + text: '自定义操作按钮', + size: TDButtonSize.medium, + theme: TDButtonTheme.danger, + width: 160, + onTap: () {}, + )), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_fab_page.dart b/tdesign-component/example/lib/page/td_fab_page.dart new file mode 100644 index 000000000..9e001cb62 --- /dev/null +++ b/tdesign-component/example/lib/page/td_fab_page.dart @@ -0,0 +1,185 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; +class TDFabPage extends StatefulWidget { + const TDFabPage({Key? key}) : super(key: key); + + @override + State createState() => _TDFabPageState(); +} + +class _TDFabPageState extends State { + bool showBorder = false; + + @override + Widget build(BuildContext context) { + return ExamplePage(title: tdTitle(), exampleCodeGroup: 'fab', children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: 'Icon Fab 纯图标悬浮按钮', builder: _buildPureIconFab), + ExampleItem( + desc: 'Icon Fab with Text 图标加文字悬浮按钮', builder: _buildTextFab) + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: 'Fab Theme 悬浮按钮主题', builder: _buildThemeFab), + ExampleItem(desc: 'Fab Shape 悬浮按钮形状', builder: _buildShapeFab), + ExampleItem(desc: 'Fab Size 悬浮按钮尺寸', builder: _buildSizeFab) + ]) + ]); + } + + @Demo(group: 'fab') + Widget _buildPureIconFab(BuildContext context) { + return _buildRowDemo([ + const TDFab( + theme: TDFabTheme.primary, + ) + ]); + } + + @Demo(group: 'fab') + Widget _buildTextFab(BuildContext context) { + return _buildRowDemo([ + const TDFab( + theme: TDFabTheme.primary, + text: 'Floating', + ) + ]); + } + + @Demo(group: 'fab') + Widget _buildThemeFab(BuildContext context) { + return _buildRowDemoWidthDescription([ + { + 'component': const TDFab( + theme: TDFabTheme.primary, + ), + 'desc': 'Primary' + }, + { + 'component': const TDFab( + theme: TDFabTheme.defaultTheme, + ), + 'desc': 'Default' + }, + { + 'component': const TDFab( + theme: TDFabTheme.light, + ), + 'desc': 'Light' + }, + { + 'component': const TDFab( + theme: TDFabTheme.danger, + ), + 'desc': 'Danger' + }, + ]); + } + + @Demo(group: 'fab') + Widget _buildShapeFab(BuildContext context) { + return _buildRowDemoWidthDescription([ + { + 'component': const TDFab( + theme: TDFabTheme.primary, + shape: TDFabShape.circle, + ), + 'desc': 'Circle' + }, + { + 'component': const TDFab( + theme: TDFabTheme.primary, + shape: TDFabShape.square, + ), + 'desc': 'Square' + }, + ]); + } + + @Demo(group: 'fab') + Widget _buildSizeFab(BuildContext context) { + return _buildRowDemoWidthDescription([ + { + 'component': const TDFab( + theme: TDFabTheme.primary, + size: TDFabSize.large, + ), + 'desc': 'Large' + }, + { + 'component': const TDFab( + theme: TDFabTheme.primary, + size: TDFabSize.medium, + ), + 'desc': 'Medium' + }, + { + 'component': const TDFab( + theme: TDFabTheme.primary, + size: TDFabSize.small, + ), + 'desc': 'Small' + }, + { + 'component': const TDFab( + theme: TDFabTheme.primary, + size: TDFabSize.extraSmall, + ), + 'desc': 'extraSmall' + }, + ]); + } + + Widget _buildRowDemo(List fabs) { + return Padding( + padding: const EdgeInsets.only( + left: 16, + ), + child: Row( + children: fabs + .map((fab) => Padding( + padding: const EdgeInsets.only(right: 80), + child: fab, + )) + .toList(), + ), + ); + } + + Widget _buildRowDemoWidthDescription(List> fabs) { + return Padding( + padding: const EdgeInsets.only(left: 16, top: 4, right: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: fabs + .map( + (fab) => Padding( + padding: const EdgeInsets.only(right: 30), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 48, + child: Column( + children: [fab['component']], + ), + ), + const SizedBox( + height: 24, + ), + TDText( + fab['desc'], + style: TextStyle( + fontSize: 14, color: Colors.black.withOpacity(0.4)), + ) + ], + ), + ), + ) + .toList(), + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_font_page.dart b/tdesign-component/example/lib/page/td_font_page.dart new file mode 100644 index 000000000..1c796888c --- /dev/null +++ b/tdesign-component/example/lib/page/td_font_page.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +/// 字体示例页面 +class TDFontPage extends StatelessWidget { + const TDFontPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + // debugPaintBaselinesEnabled = true; + return ExamplePage(padding: const EdgeInsets.all(8), title: tdTitle(context), exampleCodeGroup: 'fonts', children: [ + ExampleModule(title: 'Token', children: [ + ExampleItem( + ignoreCode: true, + builder: (context) { + var children = []; + TDTheme.of(context).fontMap.forEach((key, value) { + children.add(Container( + child: TDText( + '@$key:${value.size.toInt()}px', + font: value, + + /// link类型的示例添加下划线 + style: TextStyle( + decoration: key.contains('Link') ? TextDecoration.underline : null, + decorationColor: TDTheme.of(context).fontGyColor1), + ), + decoration: BoxDecoration( + border: Border(bottom: BorderSide(color: TDTheme.of(context).grayColor4, width: 0.5))), + )); + }); + return ListView( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: children, + ); + }) + ]), + ], test: [ + ExampleItem( + ignoreCode: true, + builder: (context) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: TDText('使用主题字体:fontBodySmall', font: TDTheme.of(context).fontBodySmall), + ), + Padding( + padding: const EdgeInsets.all(16), + child: TDText('使用主题字体:fontBodyLarge', font: TDTheme.of(context).fontBodyLarge), + ), + Padding( + padding: const EdgeInsets.all(16), + child: TDText( + '不使用数字字体:1234567890abcd', + font: TDTheme.defaultData().fontTitleSmall, + textColor: TDTheme.of(context).brandColor6, + )), + Padding( + padding: const EdgeInsets.all(16), + child: TDText( + '使用数字字体:1234567890abcd', + font: TDTheme.defaultData().fontTitleSmall, + textColor: TDTheme.of(context).brandColor6, + fontFamily: TDTheme.defaultData().numberFontFamily, + )), + ], + ); + }) + ]); + } +} diff --git a/tdesign-component/example/lib/page/td_footer_page.dart b/tdesign-component/example/lib/page/td_footer_page.dart new file mode 100644 index 000000000..cb782993d --- /dev/null +++ b/tdesign-component/example/lib/page/td_footer_page.dart @@ -0,0 +1,107 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDFooterPage extends StatefulWidget { + const TDFooterPage({Key? key}) : super(key: key); + + @override + State createState() => _TDFooterPageState(); +} + +class _TDFooterPageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + backgroundColor: TDTheme.of(context).whiteColor1, + desc: '用于展示App的版权声明、联系信息、重要页面链接和其他相关内容等信息。', + exampleCodeGroup: 'footer', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '基础页脚', builder: _buildFooter), + ExampleItem(desc: '基础加链接页脚', builder: _buildSingleLinkFooter), + ExampleItem(desc: '', builder: _buildLinksFooter), + ExampleItem(desc: '品牌页脚', builder: _buildBrandFooter), + ], + ), + ], + ); + } + + @Demo(group: 'footer') + Widget _buildFooter(BuildContext context) { + return const TDFooter( + TDFooterType.text, + text: 'Copyright © 2019-2023 TDesign.All Rights Reserved.', + ); + } + + @Demo(group: 'footer') + Widget _buildSingleLinkFooter(BuildContext context) { + // 示例链接列表 + final singleLink = [ + TDLink( + label: '底部链接', + style: TDLinkStyle.primary, + // type: TDLinkType.withSuffixIcon, + uri: Uri.parse('https://example.com'), + linkClick: (link) { + print('点击了链接 $link'); + }, + ), + ]; + + return TDFooter( + TDFooterType.link, + links: singleLink, + text: 'Copyright © 2019-2023 TDesign.All Rights Reserved.', + ); + } + + @Demo(group: 'footer') + Widget _buildLinksFooter(BuildContext context) { + final links = [ + TDLink( + label: '底部链接1', + style: TDLinkStyle.primary, + uri: Uri.parse('https://example.com'), + linkClick: (link) { + print('点击了链接1 $link'); + }, + ), + TDLink( + label: '底部链接2', + style: TDLinkStyle.primary, + uri: Uri.parse('https://example.com'), + linkClick: (link) { + print('点击了链接2 $link'); + }, + ), + ]; + return Column( + children: [ + const SizedBox(height: 12), + TDFooter( + TDFooterType.link, + links: links, + text: 'Copyright © 2019-2023 TDesign.All Rights Reserved.', + ) + ], + ); + } + + @Demo(group: 'footer') + Widget _buildBrandFooter(BuildContext context) { + return TDFooter( + TDFooterType.brand, + logo: 'assets/img/td_brand.png', + width: 204, + height: 48, + ); + } +} diff --git a/tdesign-component/example/lib/page/td_form_page.dart b/tdesign-component/example/lib/page/td_form_page.dart new file mode 100644 index 000000000..a9b4b0995 --- /dev/null +++ b/tdesign-component/example/lib/page/td_form_page.dart @@ -0,0 +1,734 @@ +import 'dart:async'; +import 'dart:math'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +class TDFormPage extends StatefulWidget { + const TDFormPage({Key? key}) : super(key: key); + + @override + _TDFormPageState createState() => _TDFormPageState(); +} + +class _TDFormPageState extends State { + List _controller = []; + FormController _formController = FormController(); + StreamController _stepController = StreamController.broadcast(); + String _selected_1 = ''; + String _selected_2 = ''; + String? _initLocalData; + + /// form 禁用的状态 + bool _formDisableState = false; + + /// form 排列方式是否为水平 + bool _isFormHorizontal = true; + + /// 设置按钮是否可点击状态 + /// true 表示处于 active 状态 + bool horizontalButton = false; + bool verticalButton = true; + + Color activeButtonColor = Color(0xFFF0F1FD); + Color defaultButtonColor = Color(0xFFE5E5E5); + + Color verticalTextColor = Color(0xFF1A1A1A); + Color horizontalTextColor = Color(0xFF0A58D9); + Color verticalButtonColor = Color(0xFFE5E5E5); + Color horizontalButtonColor = Color(0xFFF0F1FD); + + /// radios 传入参数 + final Map _radios = {'0': '男', '1': '女', '3': '保密'}; + + /// 级联选择器 传入参数 + static const List _data = [ + { + 'label': '北京市', + 'value': '110000', + "children": [ + { + "value": '110100', + "label": '北京市', + "children": [ + {"value": '110101', "label": '东城区'}, + {"value": '1101022', "label": '东区'}, + {"value": '110102', "label": '西城区'}, + {"value": '110105', "label": '朝阳区'}, + {"value": '110106', "label": '丰台区'}, + {"value": '110107', "label": '石景山区'}, + {"value": '110108', "label": '海淀区'}, + {"value": '110109', "label": '门头沟区'}, + ], + }, + ], + }, + { + "label": '天津市', + "value": '120000', + "children": [ + { + "value": '120100', + "label": '天津市', + "children": [ + { + "value": '120101', + "label": '和平区', + }, + { + "value": '120102', + "label": '河东区', + }, + { + "value": '120103', + "label": '河西区', + }, + { + "value": '120104', + "label": '南开区', + }, + { + "value": '120105', + "label": '河北区', + }, + { + "value": '120106', + "label": '红桥区', + }, + { + "value": '120110', + "label": '东丽区', + }, + { + "value": '120111', + "label": '西青区', + }, + { + "value": '120112', + "label": '津南区', + }, + ], + }, + ], + }, + ]; + List files = []; + + ///密码是否浏览 + bool browseOn = false; + TDCheckboxGroupController _genderCheckboxGroupController = TDCheckboxGroupController(); + + /// 整个表单存放的数据 + Map _formData = { + "name": '', + "password": '', + "gender": '', + "birth": '', + "place": '', + "age": "2", + "description": "2", + "resume": '', + "photo": '', + }; + Map _formItemNotifier = { + "name": '', + "password": '', + "gender": '', + "birth": '', + "place": '', + "age": '2', + "description": '', + "resume": '', + "photo": "", + }; + + @override + void initState() { + /// 三个文本型的表格单元 + for (var i = 0; i < 4; i++) { + _controller.add(TextEditingController()); + } + _formData.forEach((key, value) { + _formItemNotifier[key] = FormItemNotifier(); + }); + super.initState(); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + _stepController.close(); + } + + /// 提交按钮钮点击事件: + /// 改变 _validate 值,从而触发校验 + /// 获取表单的数据 + void _onSubmit() { + _formController.submit(); + } + + /// 定义整个校验规则 + final Map _validationRules = { + 'name': TDFormValidation( + validate: (value) => value == null || value.isEmpty ? 'empty' : null, + errorMessage: '输入不能为空', + type: TDFormItemType.input, + ), + "password": TDFormValidation( + validate: (value) => RegExp(r'^[a-zA-Z]{8}$').hasMatch(value ?? '') ? null : 'invalid', + errorMessage: '只能输入8个字符英文', + type: TDFormItemType.input, + ), + "gender": TDFormValidation( + validate: (value) => value == null || value.isEmpty ? 'empty' : null, + errorMessage: '不能为空', + type: TDFormItemType.radios, + ), + "birth": TDFormValidation( + validate: (value) => value == null || value.isEmpty ? 'empty' : null, + errorMessage: '不能为空', + type: TDFormItemType.dateTimePicker, + ), + "place": TDFormValidation( + validate: (value) => value == null || value.isEmpty ? 'empty' : null, + errorMessage: '不能为空', + type: TDFormItemType.cascader, + ), + "age": TDFormValidation( + validate: (value) { + if (value == null || value.isEmpty) { + return 'empty'; + } else if (int.parse(value) < 3) { + return 'empty'; + } + return null; + }, + errorMessage: '输入的数字不能大于用户所填生日对应的年龄', + type: TDFormItemType.stepper, + ), + "description": TDFormValidation( + validate: (value) { + if (value == null || value.isEmpty) { + return 'empty'; + } else if (double.parse(value) < 4) { + return 'empty'; + } + return null; + }, + errorMessage: '分数过低会影响整体评价', + type: TDFormItemType.rate, + ), + "resume": TDFormValidation( + validate: (value) => value == null || value.isEmpty ? 'empty' : null, + errorMessage: '不能为空', + type: TDFormItemType.textarea, + ), + "photo": TDFormValidation( + validate: (value) => value == null || value.isEmpty ? 'empty' : null, + errorMessage: '不能为空', + type: TDFormItemType.upLoadImg, + ), + }; + + ///表单提交数据 + onSubmit(Map formData, isValidateSuc) {} + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + exampleCodeGroup: 'form', + desc: '用以收集、校验和提交数据,一般由输入框、单选框、复选框、选择器等控件组成。', + backgroundColor: const Color(0xfff6f6f6), + children: [ + ExampleModule(title: '基础类型', children: [ + ExampleItem(ignoreCode: true,desc: '基础表单', builder: _buildArrangementSwitch), + ExampleItem(ignoreCode: true,desc: '', builder: _buildSwitchWithBase), + ExampleItem(ignoreCode: true, builder: (BuildContext context) { + return CodeWrapper(builder: _buildForm); + }), + // ExampleItem(ignoreCode: true, desc: '', builder: (_) => CodeWrapper(builder: _buildCombinationButtons)), + ]), + ], + ); + } + + @Demo(group: 'form') + Widget _buildForm(BuildContext context) { + final theme = TDTheme.of(context); + return TDForm( + formController: _formController, + disabled: _formDisableState, + data: _formData, + isHorizontal: _isFormHorizontal, + rules: _validationRules, + formContentAlign: TextAlign.left, + requiredMark: true, + + /// 确定整个表单是否展示提示信息 + formShowErrorMessage: true, + onSubmit: onSubmit, + items: [ + TDFormItem( + label: '用户名', + name: 'name', + type: TDFormItemType.input, + help: '请输入用户名', + labelWidth: 82.0, + formItemNotifier: _formItemNotifier['name'], + + /// 控制单个 item 是否展示错误提醒 + showErrorMessage: true, + requiredMark: true, + child: TDInput( + leftContentSpace: 0, + inputDecoration: InputDecoration( + hintText: "请输入用户名", + border: InputBorder.none, + contentPadding: EdgeInsets.all(0), + hintStyle: TextStyle(color: TDTheme.of(context).fontGyColor3.withOpacity(0.4))), + controller: _controller[0], + backgroundColor: Colors.white, + additionInfoColor: TDTheme.of(context).errorColor6, + showBottomDivider: false, + readOnly: _formDisableState, + onChanged: (val) { + _formItemNotifier['name']?.upDataForm(val); + }, + onClearTap: () { + _controller[0].clear(); + _formItemNotifier['name']?.upDataForm(""); + }), + ), + TDFormItem( + label: '密码', + name: 'password', + type: TDFormItemType.input, + labelWidth: 82.0, + formItemNotifier: _formItemNotifier['password'], + showErrorMessage: true, + child: TDInput( + leftContentSpace: 0, + inputDecoration: InputDecoration( + hintText: '请输入密码', + border: InputBorder.none, + hintStyle: TextStyle(color: TDTheme.of(context).fontGyColor3.withOpacity(0.4))), + type: TDInputType.normal, + controller: _controller[1], + obscureText: !browseOn, + backgroundColor: Colors.white, + needClear: false, + readOnly: _formDisableState, + showBottomDivider: false, + onChanged: (val) { + _formItemNotifier['password']?.upDataForm(val); + }, + onClearTap: () { + _controller[1].clear(); + _formItemNotifier['password']?.upDataForm(""); + }), + ), + TDFormItem( + label: '性别', + name: 'gender', + type: TDFormItemType.radios, + labelWidth: 82.0, + showErrorMessage: true, + formItemNotifier: _formItemNotifier['gender'], + child: TDRadioGroup( + spacing: 0, + direction: Axis.horizontal, + controller: _genderCheckboxGroupController, + directionalTdRadios: _radios.entries.map((entry) { + return TDRadio( + id: entry.key, + title: entry.value, + radioStyle: TDRadioStyle.circle, + showDivider: false, + spacing: 4, + checkBoxLeftSpace: 0, + customSpace: EdgeInsets.all(0), + enable: !_formDisableState, + ); + }).toList(), + onRadioGroupChange: (ids) { + if (ids == null) { + return; + } + _formItemNotifier['gender']?.upDataForm(ids); + }, + ), + ), + TDFormItem( + label: '生日', + name: 'birth', + labelWidth: 82.0, + type: TDFormItemType.dateTimePicker, + contentAlign: TextAlign.left, + tipAlign: TextAlign.left, + formItemNotifier: _formItemNotifier['birth'], + hintText:'请输入内容', + select: _selected_1, + selectFn: (BuildContext context) { + if (_formDisableState) { + return; + } + TDPicker.showDatePicker(context, title: '选择时间', onConfirm: (selected) { + setState(() { + _selected_1 = + '${selected['year'].toString().padLeft(4, '0')}-${selected['month'].toString().padLeft(2, '0')}-${selected['day'].toString().padLeft(2, '0')}'; + _formItemNotifier['birth']?.upDataForm(_selected_1); + }); + Navigator.of(context).pop(); + }, dateStart: [1999, 01, 01], dateEnd: [2050, 12, 31], initialDate: [2012, 1, 1]); + }, + ), + TDFormItem( + label: '籍贯', + name: 'place', + type: TDFormItemType.cascader, + contentAlign: TextAlign.left, + tipAlign: TextAlign.left, + labelWidth: 82.0, + hintText:'请输入内容', + select: _selected_2, + formItemNotifier: _formItemNotifier['place'], + selectFn: (BuildContext context) { + if (_formDisableState) { + return; + } + TDCascader.showMultiCascader(context, + title: '选择地址', + data: _data, + initialData: _initLocalData, + theme: 'step', onChange: (List selectData) { + setState(() { + var result = []; + var len = selectData.length; + _initLocalData = selectData[len - 1].value!; + selectData.forEach((element) { + result.add(element.label); + }); + _selected_2 = result.join('/'); + _formItemNotifier['place']?.upDataForm(_selected_2); + }); + }, onClose: () { + Navigator.of(context).pop(); + }); + }, + ), + TDFormItem( + label: '年限', + name: 'age', + labelWidth: 82.0, + type: TDFormItemType.stepper, + formItemNotifier: _formItemNotifier['age'], + child: Padding( + padding: EdgeInsets.only( right: 18), + child: TDStepper( + theme: TDStepperTheme.filled, + disabled: _formDisableState, + eventController: _stepController!, + value:int.parse(_formData['age']), + onChange: (value) { + _formItemNotifier['age']?.upDataForm('${value}'); + }, + ), + )), + TDFormItem( + label: '自我评价', + name: 'description', + tipAlign: TextAlign.left, + type: TDFormItemType.rate, + labelWidth: 82.0, + formItemNotifier: _formItemNotifier['description'], + child: Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: EdgeInsets.only(right: 18), + child: TDRate( + count: 5, + value: double.parse(_formData['description']), + allowHalf: false, + disabled: _formDisableState, + onChange: (value) { + setState(() { + _formData['description'] = '${value}'; + }); + _formItemNotifier['description']?.upDataForm('${value}'); + }, + )), + ), + ), + TDFormItem( + label: '个人简介', + labelWidth: 82.0, + name: 'resume', + type: TDFormItemType.textarea, + formItemNotifier: _formItemNotifier['resume'], + child: Padding( + padding: EdgeInsets.only(top: _isFormHorizontal?0:8,bottom: 4), + child: TDTextarea( + backgroundColor: Colors.red, + padding: EdgeInsets.all(0), + hintText: '请输入个人简介', + maxLength: 500, + indicator: true, + readOnly: _formDisableState, + layout: TDTextareaLayout.vertical, + controller: _controller[2], + showBottomDivider: false, + onChanged: (value) { + _formItemNotifier['resume']?.upDataForm(value); + }, + ), + )), + TDFormItem( + label: '上传图片', + name: 'photo', + labelWidth: 82.0, + type: TDFormItemType.upLoadImg, + formItemNotifier: _formItemNotifier['photo'], + child: Padding( + padding: EdgeInsets.only(top:4,bottom: 4), + child: TDUpload( + files: files, + multiple: true, + max: 6, + onError: print, + onValidate: print, + disabled: _formDisableState, + onChange: ((imgList, type) { + if (_formDisableState) { + return; + } + files = _onValueChanged(files ?? [], imgList, type); + List imgs = files.map((e) => e.remotePath ?? e.assetPath).toList(); + setState(() { + _formItemNotifier['photo'].upDataForm(imgs.join(',')); + }); + }), + ), + )) + ], + btnGroup: [ + Container( + decoration: BoxDecoration( + color: theme.whiteColor1, + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Expanded( + child: TDButton( + text: '重置', + size: TDButtonSize.large, + type: TDButtonType.fill, + theme: TDButtonTheme.light, + shape: TDButtonShape.rectangle, + disabled: _formDisableState, + onTap: () { + //用户名称 + _controller[0].clear(); + //密码 + _controller[1].clear(); + // 性别 + _genderCheckboxGroupController.toggle('', false); + //个人简介 + _controller[2].clear(); + //生日 + _selected_1 = ''; + //籍贯 + _selected_2 = ''; + //年限 + _stepController.add(TDStepperEventType.cleanValue); + //上传图片 + files.clear(); + _formData = { + "name": '', + "password": '', + "gender": '', + "birth": '', + "place": '', + "age": "0", + "description": "2", + "resume": '', + "photo": '' + }; + _formData.forEach((key, value) { + _formItemNotifier[key].upDataForm(value); + }); + _formController.reset(_formData); + setState(() {}); + }, + )), + SizedBox( + width: 20, + ), + Expanded( + child: TDButton( + text: '提交', + size: TDButtonSize.large, + type: TDButtonType.fill, + theme: TDButtonTheme.primary, + shape: TDButtonShape.rectangle, + onTap: _onSubmit, + disabled: _formDisableState)), + ], + )), + ) + ]); + } + + List _onValueChanged(List fileList, List value, TDUploadType event) { + switch (event) { + case TDUploadType.add: + fileList.addAll(value); + break; + case TDUploadType.remove: + fileList.removeWhere((element) => element.key == value[0].key); + break; + case TDUploadType.replace: + final firstReplaceFile = value.first; + final index = fileList.indexWhere((file) => file.key == firstReplaceFile.key); + if (index != -1) { + fileList[index] = firstReplaceFile; + } + break; + } + return fileList; + } + + /// 横 竖 排版模式切换按钮 + Widget _buildArrangementSwitch(BuildContext buildContext) { + final theme = TDTheme.of(context); + return Container( + decoration: BoxDecoration( + color: theme.whiteColor1, + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + Expanded( + child: TDButton( + text: '水平排布', + shape: TDButtonShape.round, + style: TDButtonStyle(backgroundColor: horizontalButtonColor), + textStyle: TextStyle( + fontWeight: FontWeight.w700, + color: horizontalTextColor, + ), + onTap: () { + setState(() { + if (horizontalButton) { + /// 置换按钮状态 + horizontalButton = false; + verticalButton = true; + + /// 置换按钮颜色 + final currentVerticalColor = verticalButtonColor; + verticalButtonColor = horizontalButtonColor; + horizontalButtonColor = currentVerticalColor; + + /// 置换文字颜色 + final currentTextColor = verticalTextColor; + verticalTextColor = horizontalTextColor; + horizontalTextColor = currentTextColor; + _isFormHorizontal = true; + print(_isFormHorizontal); + } + }); + }, + ), + ), + const SizedBox(width: 8), + Expanded( + child: TDButton( + text: '竖直排布', + shape: TDButtonShape.round, + style: TDButtonStyle(backgroundColor: verticalButtonColor), + textStyle: TextStyle( + fontWeight: FontWeight.w700, + color: verticalTextColor, + ), + onTap: () { + setState(() { + if (verticalButton) { + /// 置换按钮状态 + horizontalButton = true; + verticalButton = false; + + /// 置换按钮颜色 + final currentVerticalColor = verticalButtonColor; + verticalButtonColor = horizontalButtonColor; + horizontalButtonColor = currentVerticalColor; + + /// 置换文字颜色 + final currentTextColor = verticalTextColor; + verticalTextColor = horizontalTextColor; + horizontalTextColor = currentTextColor; + + _isFormHorizontal = false; + print(_isFormHorizontal); + } + }); + }, + ), + ), + ], + ))); + } + + Widget _buildSwitchWithBase(BuildContext context) { + return _buildItem( + context, + TDSwitch( + isOn: _formDisableState, + onChanged: (value) { + setState(() { + _formDisableState = value; + }); + return false; + }, + ), + title: '禁用态', + ); + } + + @Demo(group: 'switch') + Widget _buildItem(BuildContext context, Widget switchItem, {String? title, String? desc}) { + final theme = TDTheme.of(context); + Widget current = Row( + children: [ + Expanded( + child: TDText( + title ?? '', + textColor: theme.fontGyColor1, + )), + TDText( + desc ?? '', + textColor: theme.grayColor6, + ), + SizedBox(child: switchItem) + ], + ); + current = Padding( + padding: const EdgeInsets.symmetric(vertical: 1), + child: SizedBox( + child: Container( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: current, + ), + color: Colors.white, + ), + height: 56, + ), + ); + return Column(mainAxisSize: MainAxisSize.min, children: [current]); + } +} diff --git a/tdesign-component/example/lib/page/td_icon_page.dart b/tdesign-component/example/lib/page/td_icon_page.dart new file mode 100644 index 000000000..dc92ecd77 --- /dev/null +++ b/tdesign-component/example/lib/page/td_icon_page.dart @@ -0,0 +1,153 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDIconPage extends StatefulWidget { + const TDIconPage({Key? key}) : super(key: key); + + @override + State createState() => _TDIconPageState(); +} + +class _TDIconPageState extends State { + bool showBorder = false; + + dynamic iconList = []; + + var isLoading = false; + + @override + void initState() { + super.initState(); + + iconList = TDIcons.all.values; + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: 'Icon 作为UI构成中重要的元素,一定程度上影响UI界面整体呈现出的风格。', + exampleCodeGroup: 'icon', + children: [ + ExampleModule( + title: 'icon示例', children: [ExampleItem(desc: 'icon数量: ${iconList.length}', builder: _showAllIcons)]) + ]); + } + + @Demo(group: 'icon') + Widget _showAllIcons(BuildContext context) { + return Container( + color: Colors.white, + alignment: Alignment.center, + child: Column( + children: [ + Container( + padding: const EdgeInsets.only(left: 16), + alignment: Alignment.topLeft, + child: const Wrap( + children: [ + TDText('筛选Icon请前往TDesign官网(长按网址可复制):'), + SelectableText('https://tdesign.tencent.com/vue/components/icon') + ], + ), + ), + TDSearchBar( + action: '搜索', + onActionClick: (text) { + setState(() { + iconList = []; + isLoading = true; + }); + Future.delayed(const Duration(milliseconds: 30), () { + var list = []; + TDIcons.all.forEach((key, value) { + if (value.name.contains(text)) { + list.add(value); + } + }); + setState(() { + iconList = list; + isLoading = false; + }); + }); + }, + onClearClick: (_) { + setState(() { + iconList = TDIcons.all.values; + }); + }, + ), + Container( + child: TDButton( + text: showBorder ? '隐藏边框' : '显示边框', + shape: TDButtonShape.filled, + onTap: () { + setState(() { + showBorder = !showBorder; + }); + }, + ), + margin: const EdgeInsets.only(bottom: 16), + ), + Builder(builder: (context) { + if (iconList.isEmpty) { + return Container( + height: 300, + alignment: Alignment.center, + child: isLoading ? const TDText('加载中...') : const TDText('暂无内容'), + ); + } + return SizedBox( + height: MediaQuery.of(context).size.height - 150, + child: ListView.builder( + itemCount: (iconList.length + 1) ~/ 2, + itemBuilder: (context,index){ + var index1 = index ~/ 2; + var index2 = index1 + 1; + var iconData1 = iconList.elementAt(index1); + var iconData2; + if(iconList.length > index2){ + iconData2 = iconList.elementAt(index2); + } + return Row( + children: [ + SizedBox( + height: 100, + width: 175, + child: Column( + children: [ + Container( + color: showBorder ? TDTheme.of(context).brandDisabledColor : Colors.transparent, + child: Icon(iconData1), + ), + TDText(iconData1.name) + ], + ), + ), + if (iconData2 != null) + SizedBox( + height: 100, + width: 175, + child: Column( + children: [ + Container( + color: showBorder ? TDTheme.of(context).brandDisabledColor : Colors.transparent, + child: Icon(iconData2), + ), + TDText(iconData2.name) + ], + ), + ) + ], + ); + }), + ); + }) + ], + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_image_page.dart b/tdesign-component/example/lib/page/td_image_page.dart new file mode 100644 index 000000000..fd08b1afe --- /dev/null +++ b/tdesign-component/example/lib/page/td_image_page.dart @@ -0,0 +1,529 @@ +import 'package:flutter/material.dart'; + +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; +import 'dart:io'; + +class TDImagePage extends StatefulWidget { + const TDImagePage({Key? key}) : super(key: key); + + @override + State createState() => TDImageState(); +} + +class TDImageState extends State + with SingleTickerProviderStateMixin { + late Animation animation; + late AnimationController animationController; + @override + void initState() { + super.initState(); + animationController = + AnimationController(vsync: this, duration: const Duration(seconds: 4)); + animation = Tween(begin: 0.0, end: 4.0).animate(animationController); + animationController.repeat(); + } + + @override + void dispose() { + animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + exampleCodeGroup: 'image', + desc: '用于展示效果,主要为上下左右居中裁切、拉伸、平铺等方式。', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem( + ignoreCode: true, + desc: '', + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 8), + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _imageClip, + ), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _imageStretch, + ), + ), + ], + ), + ); + }), + ExampleItem( + ignoreCode: true, + desc: '', + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 8), + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _imageFitHeight, + ), + ), + Container( + margin: const EdgeInsets.all(8), // 适应宽 + child: CodeWrapper( + builder: _imageFitWidth, + ), + ), + ], + ), + ); + }), + ExampleItem( + ignoreCode: true, + desc: '', + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 8), + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _imageSquare, + ), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _imageRoundedSquare, + ), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _imageCircle, + ), + ), + ], + ), + ); + }) + ], + ), + ExampleModule( + title: '组件状态', + children: [ + ExampleItem( + ignoreCode: true, + desc: '', + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 8), + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _loadingDefault, + ), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _loadingCustom, + ), + ), + ], + ), + ); + }), + ExampleItem( + ignoreCode: true, + desc: '', + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 8), + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _failDefault, + ), + ), + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _failCustom, + ), + ), + ], + ), + ); + }), + ], + ) + ], + test: [ + ExampleItem( + ignoreCode: true, + desc: '', + builder: (context) { + return Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(left: 8), + child: Wrap( + children: [ + Container( + margin: const EdgeInsets.all(8), + child: CodeWrapper( + builder: _imageFile, + ), + ), + ], + ), + ); + }), + ], + ); + } + +/* 图片裁剪 */ + @Demo(group: 'image') + Widget _imageClip(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '裁剪', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + const TDImage( + assetUrl: 'assets/img/image.png', + type: TDImageType.clip, + ), + ], + ); + } +/* 图片拉伸 */ + @Demo(group: 'image') + Widget _imageStretch(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '拉伸', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + color: Colors.black, + width: 121, + height: 72, + child: Stack( + alignment: Alignment.center, + children: [ + TDImage( + assetUrl: 'assets/img/image.png', + width: 121, + height: 50, + type: TDImageType.stretch, + ), + ], + ), + ), + ], + ); + } +/* 图片适应高 */ + @Demo(group: 'image') + Widget _imageFitHeight(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '适应高', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + width: 89, + height: 72, + color: Colors.black, + child: const TDImage( + assetUrl: 'assets/img/image.png', + type: TDImageType.fitHeight, + ), + ), + ], + ); + } + /* 图片适应宽 */ + @Demo(group: 'image') + Widget _imageFitWidth(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '适应宽', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + width: 72, + height: 89, + color: Colors.black, + child: const TDImage( + assetUrl: 'assets/img/image.png', + type: TDImageType.fitWidth, + ), + ), + ], + ); + } +/* 方形 */ + @Demo(group: 'image') + Widget _imageSquare(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '方形', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + const TDImage( + assetUrl: 'assets/img/image.png', + type: TDImageType.square, + ), + ], + ); + } +/* 圆角方形 */ + @Demo(group: 'image') + Widget _imageRoundedSquare(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '圆角方形', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + const TDImage( + assetUrl: 'assets/img/image.png', + type: TDImageType.roundedSquare, + width: 72, + height: 72, + ), + ], + ); + } +/* 圆形 */ + @Demo(group: 'image') + Widget _imageCircle(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '圆形', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + const TDImage( + assetUrl: 'assets/img/image.png', + width: 72, + height: 72, + type: TDImageType.circle, + ), + ], + ); + } +/* 加载默认提示 */ + @Demo(group: 'image') + Widget _loadingDefault(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '加载默认提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + height: 72, + width: 72, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + child: Container( + alignment: Alignment.center, + color: TDTheme.of(context).grayColor2, + child: Icon( + TDIcons.ellipsis, + size: 22, + color: TDTheme.of(context).fontGyColor3, + ))), + // 实际组件写法如下:上面仅为加载展示 + // const TDImage( + // imgUrl: + // 'https://images.pexels.com/photos/842711/pexels-photo-842711.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + // type: TDImageType.roundedSquare, + // ), + ], + ); + } +/* 加载自定义提示 */ + @Demo(group: 'image') + Widget _loadingCustom(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '加载自定义提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + Container( + height: 72, + width: 72, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + child: Container( + alignment: Alignment.center, + color: TDTheme.of(context).grayColor2, + child: RotationTransition( + turns: animation, + alignment: Alignment.center, + child: TDCircleIndicator( + color: TDTheme.of(context).brandNormalColor, + size: 18, + lineWidth: 3, + )))), + // 实际组件写法如下:上面仅为加载展示 + // TDImage( + // imgUrl: + // 'https://images.pexels.com/photos/842711/pexels-photo-842711.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2', + // loadingWidget: RotationTransition( + // turns: animation, + // alignment: Alignment.center, + // child: TDCircleIndicator( + // color: TDTheme.of(context).brandNormalColor, + // size: 18, + // lineWidth: 3, + // )), + // type: TDImageType.roundedSquare, + // ), + ], + ); + } +/* 失败默认提示 */ + @Demo(group: 'image') + Widget _failDefault(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '失败默认提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + const TDImage( + imgUrl: 'error', + type: TDImageType.roundedSquare, + ), + ], + ); + } +/* 失败自定义提示 */ + @Demo(group: 'image') + Widget _failCustom(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 16), + child: TDText( + '失败自定义提示', + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + ), + TDImage( + imgUrl: 'error', + errorWidget: TDText( + '加载失败', + forceVerticalCenter: true, + font: TDTheme.of(context).fontBodyExtraSmall, + fontWeight: FontWeight.w500, + textColor: TDTheme.of(context).fontGyColor3, + ), + type: TDImageType.roundedSquare, + ), + ], + ); + } + + @Demo(group: 'image') + Widget _imageFile(BuildContext context) { + return Container( + width: 72, + height: 72, + child: TDImage( + imageFile: File('/sdcard/td/test.jpg'), + type: TDImageType.fitWidth, + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_image_viewer_page.dart b/tdesign-component/example/lib/page/td_image_viewer_page.dart new file mode 100644 index 000000000..11ccea9c8 --- /dev/null +++ b/tdesign-component/example/lib/page/td_image_viewer_page.dart @@ -0,0 +1,208 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDImageViewerPage extends StatefulWidget { + const TDImageViewerPage({Key? key}) : super(key: key); + + @override + State createState() => _TDImageViewerPageState(); +} + +class _TDImageViewerPageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + backgroundColor: const Color(0xFFF0F2F5), + title: tdTitle(), + desc: '用于图片内容的缩略展示与查看。', + exampleCodeGroup: 'image_viewer', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '基础图片预览', builder: _basicImageViewer), + ExampleItem(desc: '带操作图片预览', builder: _actionImageViewer) + ]), + ], + test: [ + ExampleItem(desc: '长按图片', builder: _longPressImageViewer), + ExampleItem(desc: '图片超宽情况', builder: _ultraWidthImageViewer), + ExampleItem(desc: '图片超高情况', builder: _ultraHeightImageViewer), + ExampleItem(desc: '带图片标题', builder: _descImageViewer), + ], + ); + } + + var images = [ + 'https://tdesign.gtimg.com/mobile/demos/swiper1.png', + 'https://tdesign.gtimg.com/mobile/demos/swiper2.png', + ]; + + @Demo(group: 'image_viewer') + Widget _basicImageViewer(BuildContext context) { + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '基础图片预览', + onTap: () { + TDImageViewer.showImageViewer(context: context, images: images); + }, + ); + } + + @Demo(group: 'image_viewer') + Widget _actionImageViewer(BuildContext context) { + var delImages = [ + 'https://tdesign.gtimg.com/mobile/demos/swiper1.png', + 'https://tdesign.gtimg.com/mobile/demos/swiper2.png', + ]; + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '带操作图片预览', + onTap: () { + TDImageViewer.showImageViewer( + context: context, + images: delImages, + showIndex: true, + deleteBtn: true, + ); + }, + ); + } + + @Demo(group: 'image_viewer') + Widget _longPressImageViewer(BuildContext context) { + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '长按图片', + onTap: () { + TDImageViewer.showImageViewer( + context: context, + images: images, + deleteBtn: true, + showIndex: true, + onLongPress: (index) { + Navigator.of(context).push(TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: _getSheetItem, + )); + }, + ); + }, + ); + } + + @Demo(group: 'image_viewer') + Widget _ultraWidthImageViewer(BuildContext context) { + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '图片超宽情况', + onTap: () { + TDImageViewer.showImageViewer( + context: context, + images: images, + showIndex: true, + height: 140, + onLongPress: (index) { + Navigator.of(context).push(TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: _getSheetItem, + )); + }, + ); + }, + ); + } + + @Demo(group: 'image_viewer') + Widget _ultraHeightImageViewer(BuildContext context) { + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '图片超高情况', + onTap: () { + TDImageViewer.showImageViewer( + context: context, + images: images, + showIndex: true, + width: 180, + onLongPress: (index) { + Navigator.of(context).push(TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: _getSheetItem, + )); + }, + ); + }, + ); + } + + @Demo(group: 'image_viewer') + Widget _descImageViewer(BuildContext context) { + var delImages = [ + 'https://tdesign.gtimg.com/mobile/demos/swiper1.png', + 'https://tdesign.gtimg.com/mobile/demos/swiper2.png', + ]; + var labels = ['图片标题1', '图片标题2']; + return TDButton( + type: TDButtonType.ghost, + theme: TDButtonTheme.primary, + isBlock: true, + size: TDButtonSize.large, + text: '带图片标题', + onTap: () { + TDImageViewer.showImageViewer( + context: context, + images: delImages, + labels: labels, + ); + }, + ); + } + + Widget _getSheetItem(BuildContext context) { + return Container( + width: double.infinity, + height: 120, + color: Colors.white, + child: Column( + children: [ + const SizedBox(height: 16), + Text( + '保存图片', + style: TextStyle( + color: TDTheme.of(context).fontGyColor1, + decoration: TextDecoration.none, + fontSize: 14, + fontWeight: FontWeight.w400, + ), + ), + const TDDivider(margin: EdgeInsets.symmetric(vertical: 10),), + Text( + '删除图片', + style: TextStyle( + color: TDTheme.of(context).fontGyColor1, + decoration: TextDecoration.none, + fontSize: 14, + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_indexes_page.dart b/tdesign-component/example/lib/page/td_indexes_page.dart new file mode 100644 index 000000000..2d83b52b1 --- /dev/null +++ b/tdesign-component/example/lib/page/td_indexes_page.dart @@ -0,0 +1,208 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +const _list = [ + { + 'index': 'A', + 'children': ['阿坝', '阿拉善', '阿里', '安康', '安庆', '鞍山', '安顺', '安阳', '澳门'], + }, + { + 'index': 'B', + 'children': [ + '北京', + '白银', + '保定', + '宝鸡', + '保山', + '包头', + '巴中', + '北海', + '蚌埠', + '本溪', + '毕节', + '滨州', + '百色', + '亳州', + ], + }, + { + 'index': 'C', + 'children': [ + '重庆', + '成都', + '长沙', + '长春', + '沧州', + '常德', + '昌都', + '长治', + '常州', + '巢湖', + '潮州', + '承德', + '郴州', + '赤峰', + '池州', + '崇左', + '楚雄', + '滁州', + '朝阳', + ], + }, + { + 'index': 'D', + 'children': [ + '大连', + '东莞', + '大理', + '丹东', + '大庆', + '大同', + '大兴安岭', + '德宏', + '德阳', + '德州', + '定西', + '迪庆', + '东营', + ], + }, + { + 'index': 'E', + 'children': ['鄂尔多斯', '恩施', '鄂州'], + }, + { + 'index': 'F', + 'children': ['福州', '防城港', '佛山', '抚顺', '抚州', '阜新', '阜阳'], + }, + { + 'index': 'G', + 'children': ['广州', '桂林', '贵阳', '甘南', '赣州', '甘孜', '广安', '广元', '贵港', '果洛'], + }, + { + 'index': 'J', + 'children': ['揭阳', '吉林', '晋江', '吉安', '胶州', '嘉兴', '济南', '鸡西', '荆州', '江门', '基隆'], + }, + { + 'index': 'K', + 'children': ['昆明', '开封', '康定', '喀什'], + }, +]; + +class TDIndexesPage extends StatelessWidget { + const TDIndexesPage({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: TDTheme.of(context).grayColor2, + child: ExamplePage( + title: tdTitle(context), + desc: '用于页面中信息快速检索,可以根据目录中的页码快速找到所需的内容。', + exampleCodeGroup: 'indexes', + navBarKey: navBarkey, + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + ignoreCode: true, + desc: '基础索引类型', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildSimple); + }, + ), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem( + ignoreCode: true, + desc: '其他索引类型', + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildOther); + }, + ), + ]), + ], + test: const [], + )); + } +} + +@Demo(group: 'indexes') +Widget _buildSimple(BuildContext context) { + final renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + final indexList = _list.map((item) => item['index'] as String).toList(); + return TDButton( + text: '基础用法', + isBlock: true, + size: TDButtonSize.large, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + onTap: () { + Navigator.of(context).push( + TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.right, + modalTop: renderBox?.size.height, + builder: (context) { + return Container( + color: Colors.white, + child: TDIndexes( + indexList: indexList, + builderContent: (context, index) { + final list = _list.firstWhere((element) => element['index'] == index)['children'] as List; + return TDCellGroup( + cells: list + .map((e) => TDCell( + title: e, + )) + .toList(), + ); + }, + ), + ); + }, + ), + ); + }, + ); +} + +@Demo(group: 'indexes') +Widget _buildOther(BuildContext context) { + final renderBox = navBarkey.currentContext?.findRenderObject() as RenderBox?; + final indexList = _list.map((item) => item['index'] as String).toList(); + return TDButton( + text: '胶囊索引', + isBlock: true, + size: TDButtonSize.large, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + onTap: () { + Navigator.of(context).push( + TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.right, + modalTop: renderBox?.size.height, + builder: (context) { + return Container( + color: Colors.white, + child: TDIndexes( + indexList: indexList, + capsuleTheme: true, + builderContent: (context, index) { + final list = _list.firstWhere((element) => element['index'] == index)['children'] as List; + return TDCellGroup( + cells: list + .map((e) => TDCell( + title: e, + )) + .toList(), + ); + }, + ), + ); + }, + ), + ); + }, + ); +} \ No newline at end of file diff --git a/tdesign-component/example/lib/page/td_input_page.dart b/tdesign-component/example/lib/page/td_input_page.dart new file mode 100644 index 000000000..9088f7a14 --- /dev/null +++ b/tdesign-component/example/lib/page/td_input_page.dart @@ -0,0 +1,957 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; +import 'dart:async'; + +class TDInputViewPage extends StatefulWidget { + const TDInputViewPage({Key? key}) : super(key: key); + + @override + _TDInputViewPageState createState() => _TDInputViewPageState(); +} + +class _TDInputViewPageState extends State { + String inputText = '请输入...'; + var controller = []; + var browseOn = false; + var confirmText = '发送验证码'; + var countDownText = '重发'; + Timer? _timer; + int _countdownTime = 0; + + @override + void initState() { + for (var i = 0; i < 28; i++) { + controller.add(TextEditingController()); + } + super.initState(); + } + + @override + void dispose() { + super.dispose(); + if (_timer != null) { + _timer!.cancel(); + } + } + + void startCountdownTimer() { + const oneSec = Duration(seconds: 1); + var callback = (timer) => { + setState(() { + if (_countdownTime < 1) { + _timer?.cancel(); + } else { + _countdownTime = _countdownTime - 1; + } + }) + }; + _timer = Timer.periodic(oneSec, callback); + } + + @override + Widget build(BuildContext context) { + var childBuilder = (context){ + return ExamplePage( + backgroundColor: const Color(0xFFF0F2F5), + title: tdTitle(), + desc: '用于在预设的一组选项中执行单项选择,并呈现选择结果。', + exampleCodeGroup: 'input', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '基础输入框', builder: _basicTypeBasic), + ExampleItem(builder: _basicTypeRequire), + ExampleItem(builder: _basicTypeOptional), + ExampleItem(builder: _basicTypePureInput), + ExampleItem(builder: _basicTypeAdditionalDesc), + ExampleItem(desc: '带字数限制输入框', builder: _basicTypeTextLimit), + ExampleItem(builder: _basicTypeTextLimitChinese2), + ExampleItem(desc: '带操作输入框', builder: _basicTypeWithHandleIconOne), + ExampleItem(builder: _basicTypeWithHandleIconTwo), + ExampleItem(builder: _basicTypeWithHandleIconThree), + ExampleItem(desc: '带图标输入框', builder: _basicTypeWithLeftIconLeftLabel), + ExampleItem(builder: _basicTypeWithLeftIcon), + ExampleItem(desc: '特定类型输入框', builder: _specialTypePassword), + ExampleItem(builder: _specialTypeVerifyCode), + ExampleItem(builder: _specialTypePhoneNumber), + ExampleItem(builder: _specialTypePrice), + ExampleItem(builder: _specialTypeNumber), + ExampleItem(builder: (context) { + return Container(); + }), + ], + ), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '输入框状态', builder: _inputStatusAdditionInfo), + ExampleItem(builder: _inputStatusReadOnly), + ExampleItem(desc: '信息超长状态', builder: _inputStatusLongLabel), + ExampleItem(builder: _inputStatusLongInput), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem(desc: '内容位置', builder: _contentLeft), + ExampleItem(builder: _contentCenter), + ExampleItem(builder: _contentRight), + ExampleItem(desc: '竖排样式', builder: _verticalStyle), + ExampleItem(desc: '非通栏样式', builder: _cardStyle), + ExampleItem(desc: '标签外置样式', builder: _labelOutStyle), + ExampleItem(desc: '自定义样式输入框', builder: _customStyle), + ]), + ], + test: [ + ExampleItem(desc: '长文本样式', builder: _customLongTextStyle), + ExampleItem(desc: '隐藏底部分割线', builder: _hideBottomDivider), + ExampleItem(desc: '自定义高度-使用SizeBox', builder: _customHeight), + ExampleItem(desc: '获取焦点时点击外部区域事件响应-onTapOutside', builder: _onTapOutside), + ExampleItem(desc: '设置contentPadding内容与分割线对齐', builder: _contentPadding) + ], + ); + }; + if(PlatformUtil.isWeb){ + return FutureBuilder(future: awaitFontLoad(), builder: (context, s){ + if(s.data == null){ + return Container(); + } + return childBuilder.call(context); + }); + } + return childBuilder.call(context); + } + + @Demo(group: 'input') + Widget _basicTypeBasic(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: 'Label Text', + controller: controller[0], + backgroundColor: Colors.white, + hintText: 'Please enter text', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[0].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _basicTypeRequire(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '标签文字', + required: true, + controller: controller[1], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[1].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _basicTypeOptional(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '标签文字', + controller: controller[2], + backgroundColor: Colors.white, + hintText: '请输入文字(选填)', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[2].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _basicTypePureInput(BuildContext context) { + return Column( + children: [ + TDInput( + controller: controller[3], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[3].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _basicTypeAdditionalDesc(BuildContext context) { + return TDInput( + type: TDInputType.normal, + leftLabel: '标签文字', + controller: controller[4], + hintText: '请输入文字', + additionInfo: '辅助说明', + backgroundColor: Colors.white, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[4].clear(); + setState(() {}); + }, + ); + } + + @Demo(group: 'input') + Widget _basicTypeTextLimit(BuildContext context) { + return Column( + children: [ + TDInput( + type: TDInputType.normal, + leftLabel: '标签文字', + controller: controller[5], + hintText: '请输入文字', + maxLength: 10, + additionInfo: '最大输入10个字符', + backgroundColor: Colors.white, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[5].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _basicTypeTextLimitChinese2(BuildContext context) { + return TDInput( + type: TDInputType.normal, + leftLabel: '标签文字', + controller: controller[6], + hintText: '请输入文字', + inputFormatters: [Chinese2Formatter(10)], + additionInfo: '最大输入10个字符,汉字算两个', + backgroundColor: Colors.white, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[6].clear(); + setState(() {}); + }, + ); + } + + @Demo(group: 'input') + Widget _basicTypeWithHandleIconOne(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '标签文字', + controller: controller[7], + backgroundColor: Colors.white, + hintText: '请输入文字', + rightBtn: Icon( + TDIcons.error_circle_filled, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + TDToast.showText('点击右侧按钮', context: context); + }, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[7].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _basicTypeWithHandleIconTwo(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '标签文字', + controller: controller[8], + backgroundColor: Colors.white, + hintText: '请输入文字', + rightBtn: Container( + alignment: Alignment.center, + width: 73, + height: 28, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + color: TDTheme.of(context).brandNormalColor, + ), + child: const TDButton( + text: '操作按钮', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + ), + ), + onBtnTap: () { + TDToast.showText('点击操作按钮', context: context); + }, + needClear: false, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _basicTypeWithHandleIconThree(BuildContext context) { + return TDInput( + leftLabel: '标签文字', + controller: controller[9], + backgroundColor: Colors.white, + hintText: '请输入文字', + rightBtn: Icon( + TDIcons.user_avatar, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + TDToast.showText('点击操作按钮', context: context); + }, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[9].clear(); + setState(() {}); + }, + ); + } + + @Demo(group: 'input') + Widget _basicTypeWithLeftIconLeftLabel(BuildContext context) { + return Column( + children: [ + TDInput( + leftIcon: const Icon(TDIcons.app), + leftLabel: '标签文字', + controller: controller[10], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[10].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _basicTypeWithLeftIcon(BuildContext context) { + return Column( + children: [ + TDInput( + leftIcon: const Icon(TDIcons.app), + controller: controller[11], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[11].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _specialTypePassword(BuildContext context) { + return Column( + children: [ + TDInput( + type: TDInputType.normal, + controller: controller[12], + obscureText: !browseOn, + leftLabel: '输入密码', + hintText: '请输入密码', + backgroundColor: Colors.white, + rightBtn: browseOn + ? Icon( + TDIcons.browse, + color: TDTheme.of(context).fontGyColor3, + ) + : Icon( + TDIcons.browse_off, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + setState(() { + browseOn = !browseOn; + }); + }, + needClear: false, + ), + const SizedBox( + height: 16, + ), + ], + ); + } + + @Demo(group: 'input') + Widget _specialTypeVerifyCode(BuildContext context) { + return Column( + children: [ + TDInput( + type: TDInputType.normal, + size: TDInputSize.small, + controller: controller[13], + leftLabel: '验证码', + hintText: '输入验证码', + backgroundColor: Colors.white, + rightBtn: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 0.5, + height: 24, + color: TDTheme.of(context).grayColor3, + ), + const SizedBox( + width: 16, + ), + Image.network( + 'https://img2018.cnblogs.com/blog/736399/202001/736399-20200108170302307-1377487770.jpg', + width: 72, + height: 36, + ) + ], + ), + needClear: false, + onBtnTap: () { + TDToast.showText('点击更换验证码', context: context); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _specialTypePhoneNumber(BuildContext context) { + return Column( + children: [ + TDInput( + type: TDInputType.normal, + controller: controller[14], + leftLabel: '手机号', + hintText: '输入手机号', + backgroundColor: Colors.white, + rightBtn: SizedBox( + width: 98, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(right: 16), + child: Container( + width: 0.5, + height: 24, + color: TDTheme.of(context).grayColor3, + ), + ), + _countdownTime > 0 + ? TDText( + '${countDownText}(${_countdownTime}秒)', + textColor: TDTheme.of(context).fontGyColor4, + ) + : TDText(confirmText, textColor: TDTheme.of(context).brandNormalColor), + ], + ), + ), + needClear: false, + onBtnTap: () { + if (_countdownTime == 0) { + TDToast.showText('点击了发送验证码', context: context); + setState(() { + _countdownTime = 60; + }); + startCountdownTimer(); + } + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _specialTypePrice(BuildContext context) { + return Column( + children: [ + TDInput( + type: TDInputType.special, + controller: controller[15], + leftLabel: '价格', + hintText: '0.00', + backgroundColor: Colors.white, + textAlign: TextAlign.end, + rightWidget: TDText('元', textColor: TDTheme.of(context).fontGyColor1), + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _specialTypeNumber(BuildContext context) { + return TDInput( + type: TDInputType.special, + controller: controller[16], + leftLabel: '数量', + hintText: '填写个数', + backgroundColor: Colors.white, + textAlign: TextAlign.end, + rightWidget: TDText('个', textColor: TDTheme.of(context).fontGyColor1), + ); + } + + @Demo(group: 'input') + Widget _inputStatusAdditionInfo(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '标签文字', + controller: controller[17], + backgroundColor: Colors.white, + hintText: '请输入文字', + additionInfo: '错误提示说明', + additionInfoColor: TDTheme.of(context).errorColor6, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[17].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _inputStatusReadOnly(BuildContext context) { + return TDInput( + leftLabel: '标签文字', + readOnly: true, + // 不可编辑文字 则不必带入controller + backgroundColor: Colors.white, + hintText: '不可编辑文字', + ); + } + + @Demo(group: 'input') + Widget _inputStatusLongLabel(BuildContext context) { + return Column( + children: [ + TDInput( + leftInfoWidth: 80, + spacer: TDInputSpacer(iconLabelSpace: 4), + leftLabel: '标签超长时最多十个字', + controller: controller[18], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[18].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _inputStatusLongInput(BuildContext context) { + return TDInput( + type: TDInputType.normal, + leftLabel: '标签文字', + controller: controller[19], + backgroundColor: Colors.white, + hintText: '输入文字超长不超过两行输入文字超长不超过两行', + hintTextStyle: TextStyle( + color: TDTheme.of(context).fontGyColor1, + ), + maxLines: 2, + ); + } + + @Demo(group: 'input') + Widget _verticalStyle(BuildContext context) { + return TDInput( + spacer: TDInputSpacer(iconLabelSpace: 0), + type: TDInputType.twoLine, + leftLabel: '标签文字', + controller: controller[20], + hintText: '请输入文字', + backgroundColor: Colors.white, + rightBtn: Icon( + TDIcons.error_circle_filled, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + TDToast.showText('点击右侧按钮', context: context); + }, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[20].clear(); + setState(() {}); + }, + ); + } + + @Demo(group: 'input') + Widget _cardStyle(BuildContext context) { + return TDInput( + type: TDInputType.cardStyle, + width: MediaQuery.of(context).size.width - 32, + leftLabel: '标签文字', + controller: controller[21], + hintText: '请输入文字', + backgroundColor: Colors.white, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[21].clear(); + setState(() {}); + }, + ); + } + + @Demo(group: 'input') + Widget _labelOutStyle(BuildContext context) { + return Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(top: 16, bottom: 24), + width: MediaQuery.of(context).size.width, + color: Colors.white, + child: TDInput( + type: TDInputType.cardStyle, + cardStyle: TDCardStyle.topText, + width: MediaQuery.of(context).size.width - 32, + cardStyleTopText: '标签文字', + controller: controller[22], + hintText: '请输入文字', + rightBtn: Icon( + TDIcons.error_circle_filled, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + TDToast.showText('点击右侧按钮', context: context); + }, + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[22].clear(); + setState(() {}); + }, + ), + ); + } + + @Demo(group: 'input') + Widget _contentLeft(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '左对齐', + controller: controller[23], + backgroundColor: Colors.white, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[23].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _contentCenter(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '居中', + controller: controller[24], + backgroundColor: Colors.white, + contentAlignment: TextAlign.center, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[24].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _contentRight(BuildContext context) { + return Column( + children: [ + TDInput( + leftLabel: '右对齐', + controller: controller[25], + backgroundColor: Colors.white, + contentAlignment: TextAlign.end, + hintText: '请输入文字', + onChanged: (text) { + setState(() {}); + }, + onClearTap: () { + controller[25].clear(); + setState(() {}); + }, + ), + const SizedBox( + height: 16, + ) + ], + ); + } + + @Demo(group: 'input') + Widget _customStyle(BuildContext context) { + return TDInput( + leftLabel: '标签文字', + controller: controller[26], + backgroundColor: TDTheme.of(context).grayColor12, + leftLabelStyle: TextStyle(color: TDTheme.of(context).fontWhColor1), + textStyle: TextStyle(color: TDTheme.of(context).fontWhColor1), + hintText: '请输入文字', + hintTextStyle: TextStyle(color: TDTheme.of(context).fontWhColor3), + onChanged: (text) { + setState(() {}); + }, + clearBtnColor: TDTheme.of(context).fontWhColor3, + onClearTap: () { + controller[26].clear(); + setState(() {}); + }, + ); + } + + @Demo(group: 'input') + Widget _customLongTextStyle(BuildContext context) { + var controller = TextEditingController(); + return Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(top: 16, bottom: 24), + width: MediaQuery.of(context).size.width, + color: Colors.white, + child: TDInput( + type: TDInputType.longText, + cardStyle: TDCardStyle.topText, + width: MediaQuery.of(context).size.width - 32, + cardStyleTopText: '标签文字', + controller: controller, + hintText: '请输入文字', + rightBtn: Icon( + TDIcons.error_circle_filled, + color: TDTheme.of(context).fontGyColor3, + ), + onBtnTap: () { + TDToast.showText('点击右侧按钮', context: context); + } + ), + ); + } + + @Demo(group: 'input') + Widget _hideBottomDivider(BuildContext context) { + var controller = TextEditingController(); + return TDInput( + leftLabel: '标签文字', + controller: controller, + backgroundColor: Colors.white, + hintText: '请输入文字', + showBottomDivider: false, + ); + } + + @Demo(group: 'input') + Widget _customHeight(BuildContext context) { + var controller = TextEditingController(); + return Container( + color: Colors.yellow, + alignment: Alignment.center, + height: 90, + child: SizedBox( + height: 60, + child: TDInput( + size: TDInputSize.small, + leftLabel: '标签文字', + controller: controller, + backgroundColor: Colors.white, + hintText: '请输入文字', + needClear: true, + ), + ), + ); + } + + @Demo(group: 'input') + Widget _onTapOutside(BuildContext context) { + var controller = TextEditingController(); + return Container( + color: Colors.yellow, + alignment: Alignment.center, + height: 90, + child: SizedBox( + height: 60, + child: TDInput( + size: TDInputSize.small, + leftLabel: '标签文字', + controller: controller, + backgroundColor: Colors.white, + hintText: '请输入文字', + onTapOutside: (event) { + TDToast.showText('点击输入框外部区域', context: context); + print('on tap outside ${event}'); + }, + ), + ), + ); + } + + @Demo(group: 'input') + Widget _contentPadding(BuildContext context) { + var controller = TextEditingController(); + return Container( + color: Colors.yellow, + alignment: Alignment.center, + child: Column( + children: [ + TDInput( + size: TDInputSize.small, + controller: controller, + backgroundColor: Colors.white, + contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10), + hintText: '请输入文字', + ), + TDInput( + type: TDInputType.twoLine, + size: TDInputSize.small, + controller: controller, + backgroundColor: Colors.white, + contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 50), + hintText: '请输入文字', + ), + TDInput( + type: TDInputType.normalMaxTwoLine, + size: TDInputSize.small, + controller: controller, + backgroundColor: Colors.white, + contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 70), + hintText: '请输入文字', + ), + ], + ), + ); + } + + Future awaitFontLoad() async { + // 等待500ms,让字体加载完成 + await Future.delayed(const Duration(milliseconds: 1000)); + return true; + } +} diff --git a/tdesign-component/example/lib/page/td_link_page.dart b/tdesign-component/example/lib/page/td_link_page.dart new file mode 100644 index 000000000..d918fe81e --- /dev/null +++ b/tdesign-component/example/lib/page/td_link_page.dart @@ -0,0 +1,181 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDLinkViewPage extends StatefulWidget { + const TDLinkViewPage({Key? key}) : super(key: key); + + @override + _TDLinkViewPageState createState() => _TDLinkViewPageState(); +} + +class _TDLinkViewPageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + backgroundColor: const Color(0xFFF0F2F5), + title: tdTitle(), + desc: '当功能使用图标即可表意清楚时,可使用纯图标悬浮按钮,例如:添加、发布。', + exampleCodeGroup: 'link', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '基础文字链接', builder: _basicTypeBasic), + ExampleItem(desc: '下划线文字链接', builder: _withUnderline), + ExampleItem(desc: '前置图标文字链接', builder: _withPrefixIcon), + ExampleItem(desc: '后置图标文字链接', builder: _withSuffixIcon), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '不同主题', builder: _buildLinkStats), + ExampleItem(desc: '禁用状态', builder: _buildDisabledLinkStats) + ]), + ExampleModule( + title: '组件样式', + children: [ExampleItem(desc: '链接尺寸', builder: _buildLinkSizes)]), + ]); + } + + @Demo(group: 'link') + Widget _basicTypeBasic(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLinksWithType(TDLinkType.basic), + )); + } + + List _buildLinksWithType(TDLinkType type) { + return [ + TDLink( + label: '跳转链接', + style: TDLinkStyle.primary, + type: type, + size: TDLinkSize.small, + ), + const SizedBox( + height: 48, + width: 80, + ), + TDLink( + label: '跳转链接', + style: TDLinkStyle.defaultStyle, + type: type, + size: TDLinkSize.small, + ), + const SizedBox( + height: 16, + ), + ]; + } + + @Demo(group: 'link') + Widget _withUnderline(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLinksWithType(TDLinkType.withUnderline), + )); + } + + @Demo(group: 'link') + Widget _withSuffixIcon(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLinksWithType(TDLinkType.withSuffixIcon), + )); + } + + @Demo(group: 'link') + Widget _withPrefixIcon(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: _buildLinksWithType(TDLinkType.withPrefixIcon), + )); + } + + @Demo(group: 'link') + Widget _buildLinkStats(BuildContext context) { + return _buildLinkWithStyles(TDLinkState.normal); + } + + @Demo(group: 'link') + Widget _buildDisabledLinkStats(BuildContext context) { + return _buildLinkWithStyles(TDLinkState.disabled); + } + + Column _buildLinkWithStyles(TDLinkState state) { + return Column( + children: [ + Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildLinkWithTypeAndState(TDLinkStyle.primary, state), + const SizedBox(height: 48, width: 50), + _buildLinkWithTypeAndState(TDLinkStyle.defaultStyle, state), + const SizedBox(height: 48, width: 50), + _buildLinkWithTypeAndState(TDLinkStyle.danger, state), + ], + )), + const SizedBox(height: 16), + Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildLinkWithTypeAndState(TDLinkStyle.warning, state), + const SizedBox(height: 48, width: 50), + _buildLinkWithTypeAndState(TDLinkStyle.success, state), + ], + )), + ], + ); + } + + TDLink _buildLinkWithTypeAndState(TDLinkStyle style, TDLinkState state) { + return TDLink( + label: '跳转链接', + style: style, + state: state, + type: TDLinkType.withSuffixIcon, + size: TDLinkSize.small, + ); + } + + @Demo(group: 'link') + Widget _buildLinkSizes(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _buildLinkWithSizeAndStyle(TDLinkStyle.primary, TDLinkSize.small), + const SizedBox(height: 48, width: 40), + _buildLinkWithSizeAndStyle(TDLinkStyle.primary, TDLinkSize.medium), + const SizedBox(height: 48, width: 40), + _buildLinkWithSizeAndStyle(TDLinkStyle.primary, TDLinkSize.large), + ], + )); + } + + TDLink _buildLinkWithSizeAndStyle(TDLinkStyle style, TDLinkSize size) { + var s = size == TDLinkSize.small + ? 'S' + : (size == TDLinkSize.medium ? 'M' : 'L'); + return TDLink( + label: '${s}号链接', + style: style, + state: TDLinkState.normal, + type: TDLinkType.withSuffixIcon, + size: size, + ); + } +} diff --git a/tdesign-component/example/lib/page/td_loading_page.dart b/tdesign-component/example/lib/page/td_loading_page.dart new file mode 100644 index 000000000..f13013582 --- /dev/null +++ b/tdesign-component/example/lib/page/td_loading_page.dart @@ -0,0 +1,326 @@ +/* + * Created by haozhicao@tencent.com on 6/28/22. + * td_loading_page.dart + * + */ + +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDLoadingPage extends StatefulWidget { + const TDLoadingPage({Key? key}) : super(key: key); + + @override + State createState() => _TDLoadingPageState(); +} + +class _TDLoadingPageState extends State { + var rowSpace = const SizedBox( + width: 52, + ); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + backgroundColor: TDTheme.of(context).whiteColor1, + exampleCodeGroup: 'loading', + desc: '用于表示页面或操作的加载状态,给予用户反馈的同时减缓等待的焦虑感,由一个或一组反馈动效组成。', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '纯图标', builder: _buildPureIconLoading), + ExampleItem(desc: '图标加文字横向', builder: _buildTextIconHorizontalLoading), + ExampleItem(desc: '图标加文字竖向', builder: _buildTextIconVerticalLoading), + ExampleItem(desc: '纯文字', builder: _buildPureTextLoading), + ]), + ExampleModule(title: '组件尺寸', children: [ + ExampleItem(desc: '大尺寸', builder: _buildLargeLoading), + ExampleItem(desc: '中尺寸', builder: _buildMediumLoading), + ExampleItem(desc: '小尺寸', builder: _buildSmallLoading), + ]), + ExampleModule(title: '加载速度', children: [ + ExampleItem(desc: '调整加载速度', builder: _buildCustomSpeedLoading), + ]), + ], + test: [ + ExampleItem( + desc: '带图标的失败横向Loading', + ignoreCode: true, + builder: (_) { + return Padding( + padding: const EdgeInsets.all(16), + child: TDLoading( + icon: TDLoadingIcon.circle, + size: TDLoadingSize.small, + axis: Axis.horizontal, + text: '加载失败', + refreshWidget: GestureDetector( + child: TDText( + '刷新', + font: TDTheme.of(context).fontBodySmall, + textColor: TDTheme.of(context).brandNormalColor, + ), + onTap: () { + TDToast.showText('刷新', context: context); + }, + ), + ), + ); + }), + ExampleItem( + desc: '带图标的失败竖向Loading', + ignoreCode: true, + builder: (_) { + return Container( + padding: const EdgeInsets.all(16), + child: TDLoading( + icon: TDLoadingIcon.circle, + size: TDLoadingSize.small, + text: '加载失败', + refreshWidget: GestureDetector( + child: TDText( + '刷新', + font: TDTheme.of(context).fontBodySmall, + textColor: TDTheme.of(context).brandNormalColor, + ), + onTap: () { + TDToast.showText('刷新', context: context); + }, + ), + ), + ); + }), + ExampleItem( + desc: '验证居中问题', + ignoreCode: true, + builder: (_) { + var list = const [ + TDLoading( + size: TDLoadingSize.large, + icon: TDLoadingIcon.circle, + text: '加载中…', + axis: Axis.vertical, + ), + TDLoading( + size: TDLoadingSize.large, + icon: TDLoadingIcon.activity, + text: '加载中…', + axis: Axis.vertical, + ), + ]; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + children: list.fold( + [], + (previousValue, element) => [ + ...previousValue, + Container( + color: TDTheme.of(context).grayColor6, + child: element, + ), + rowSpace + ]))); + }), + ExampleItem( + desc: '展示/隐藏Loading', + ignoreCode: true, + builder: (_) { + var list = [ + TDButton( + text: '展示Loading', + theme: TDButtonTheme.primary, + onTap: () { + TDLoadingController.show(context); + }, + ), + const SizedBox(width: 24,), + const TDButton( + text: '隐藏Loading', + theme: TDButtonTheme.primary, + onTap: TDLoadingController.dismiss, + ), + ]; + return Padding(padding: const EdgeInsets.symmetric(horizontal: 16), child: Row(children: list, )); + }) + ], + ); + } + + @Demo(group: 'loading') + Widget _buildRow(List list) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row(children: list.fold([], (previousValue, element) => [...previousValue, element, rowSpace])),)); + } + + /// 纯图标 + @Demo(group: 'loading') + Widget _buildPureIconLoading(BuildContext context) { + return _buildRow([ + const TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + ), + const TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.activity, + ), + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.point, + iconColor: TDTheme.of(context).brandNormalColor, + ), + ]); + } + + /// 图标加文字横向 + @Demo(group: 'loading') + Widget _buildTextIconHorizontalLoading(BuildContext context) { + return _buildRow(const [ + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + text: '加载中…', + axis: Axis.horizontal, + ), + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.activity, + text: '加载中…', + axis: Axis.horizontal, + ), + ]); + } + + /// 图标加文字竖向 + @Demo(group: 'loading') + Widget _buildTextIconVerticalLoading(BuildContext context) { + return _buildRow(const [ + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + text: '加载中…', + axis: Axis.vertical, + ), + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.activity, + text: '加载中…', + axis: Axis.vertical, + ), + ]); + } + + /// 纯文字 + @Demo(group: 'loading') + Widget _buildPureTextLoading(BuildContext context) { + return _buildRow([ + const TDLoading( + size: TDLoadingSize.small, + text: '加载中…', + ), + TDLoading( + size: TDLoadingSize.small, + text: '加载失败', + textColor: TDTheme.of(context).fontGyColor3, + ), + TDLoading( + size: TDLoadingSize.small, + text: '加载失败', + refreshWidget: GestureDetector( + child: TDText( + '刷新', + font: TDTheme.of(context).fontBodySmall, + textColor: TDTheme.of(context).brandNormalColor, + ), + onTap: () { + TDToast.showText('刷新', context: context); + }, + ), + ), + ]); + } + + /// 大尺寸 + @Demo(group: 'loading') + Widget _buildLargeLoading(BuildContext context) { + return _buildRow([ + const TDLoading( + size: TDLoadingSize.large, + icon: TDLoadingIcon.circle, + text: '加载中…', + axis: Axis.horizontal, + ), + ]); + } + + /// 中尺寸 + @Demo(group: 'loading') + Widget _buildMediumLoading(BuildContext context) { + return _buildRow([ + const TDLoading( + size: TDLoadingSize.medium, + icon: TDLoadingIcon.circle, + text: '加载中…', + axis: Axis.horizontal, + ), + ]); + } + + /// 小尺寸 + @Demo(group: 'loading') + Widget _buildSmallLoading(BuildContext context) { + return _buildRow([ + const TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + text: '加载中…', + axis: Axis.horizontal, + ), + ]); + } + + double _currentSliderValue = 1000; + + /// 自定义尺寸 + @Demo(group: 'loading') + Widget _buildCustomSpeedLoading(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TDLoading( + size: TDLoadingSize.small, + icon: TDLoadingIcon.circle, + axis: Axis.horizontal, + text: '加载中…', + duration: _currentSliderValue.round(), + ), + TDSlider(value: _currentSliderValue, + sliderThemeData: TDSliderThemeData( + context: context, + max: 2000, + min: -20, + divisions: 100, + showThumbValue: true, + scaleFormatter: (value) => value.toInt().toString(), + ), + onChanged: (double value) { + setState(() { + _currentSliderValue = value; + }); + },) + ], + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_message_page.dart b/tdesign-component/example/lib/page/td_message_page.dart new file mode 100644 index 000000000..217214881 --- /dev/null +++ b/tdesign-component/example/lib/page/td_message_page.dart @@ -0,0 +1,250 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDMessagePage extends StatefulWidget { + const TDMessagePage({Key? key}) : super(key: key); + + @override + State createState() => _TDMessagePageState(); +} + +class _TDMessagePageState extends State { + final _commonContent = '这是一条普通的通知信息'; + final longContent = '这是一条普通的通知信息看,这是一条普通的通知信息,这是一条普通的通知信息'; + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + backgroundColor: Colors.white, + desc: '用于轻量级反馈或提示,不会打断用户操作。', + exampleCodeGroup: 'message', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '纯文字的通知', builder: _buildPlainTextMessage), + ExampleItem(desc: '带图标的通知', builder: _buildIconTextMessage), + ExampleItem(desc: '带关闭的通知', builder: _buildMessageWithCloseButton), + ExampleItem(desc: '可滚动的通知', builder: _buildRollingMessage), + ExampleItem(desc: '带按钮的通知', builder: _buildLinkMessage), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '普通通知', builder: _buildInfoMessage), + ExampleItem(desc: '成功通知', builder: _buildSuccessMessage), + ExampleItem(desc: '警示通知', builder: _buildWarningMessage), + ExampleItem(desc: '错误通知', builder: _buildErrorMessage), + ]), + ]); + } + + @Demo(group: 'message') + Widget _buildPlainTextMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '纯文字的通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + content: _commonContent, + visible: true, + icon: false, + theme: MessageTheme.info, + duration: 3000, + onDurationEnd: () { + print('message end'); + }, + ); + }, + ); + } + + @Demo(group: 'message') + Widget _buildIconTextMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '带图标的通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + content: _commonContent, + visible: true, + icon: true, + theme: MessageTheme.info, + duration: 3000, + ); + }); + } + + @Demo(group: 'message') + Widget _buildMessageWithCloseButton(BuildContext context) { + return TDButton( + isBlock: true, + text: '带关闭的通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.info, + duration: 300000, + closeBtn: true, + link: MessageLink(name: '按钮', uri: Uri.parse('www.example.com')), + onCloseBtnClick: () { + print('Close button clicked!'); + }, + ); + }); + } + + @Demo(group: 'message') + Widget _buildRollingMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '可滚动的通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: false, + marquee: MessageMarquee(speed: 5000, loop: 1, delay: 300), + content: longContent, + theme: MessageTheme.info, + duration: 8000, + onCloseBtnClick: () { + print('Close button clicked!'); + }); + }); + } + + @Demo(group: 'message') + Widget _buildLinkMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '带按钮的通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.info, + duration: 3000, + link: MessageLink( + name: '按钮', + uri: Uri.parse('https://tdesign.tencent.com/'), + ), + // link: '按钮', + onLinkClick: () { + print('link clicked!'); + }); + }); + } + + @Demo(group: 'message') + Widget _buildInfoMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '普通通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.info, + duration: 3000, + ); + }, + ); + } + + @Demo(group: 'message') + Widget _buildSuccessMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '成功通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.success, + duration: 3000, + ); + }, + ); + } + + @Demo(group: 'message') + Widget _buildWarningMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '警示通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.warning, + duration: 3000, + ); + }); + } + + @Demo(group: 'message') + Widget _buildErrorMessage(BuildContext context) { + return TDButton( + isBlock: true, + text: '错误通知', + size: TDButtonSize.large, + type: TDButtonType.outline, + width: 450, + theme: TDButtonTheme.primary, + onTap: () { + TDMessage.showMessage( + context: context, + visible: true, + icon: true, + content: _commonContent, + theme: MessageTheme.error, + duration: 3000, + ); + }); + } +} diff --git a/tdesign-component/example/lib/page/td_navbar_page.dart b/tdesign-component/example/lib/page/td_navbar_page.dart new file mode 100644 index 000000000..99161137a --- /dev/null +++ b/tdesign-component/example/lib/page/td_navbar_page.dart @@ -0,0 +1,283 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +const titleText = '标题文字'; + +class TDNavBarPage extends StatelessWidget { + const TDNavBarPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(context), + exampleCodeGroup: 'navbar', + desc: '用于不同页面之间切换或者跳转,位于内容区的上方,系统状态栏的下方。', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem( + desc: '基础H5导航栏', + builder: _baseH5Navbar, + ), + ExampleItem( + builder: _leftMultiAction + ), + ExampleItem( + builder: _rightMultiAction, + ), + ExampleItem( + desc: '带搜索导航栏', + builder: _searchNavbar, + ), + ExampleItem( + desc: '带图片导航栏', + builder: _logoNavbar + ), + ], + ), + ExampleModule( + title: '组件样式', + children: [ + ExampleItem( + desc: '标题对齐', + builder: _titleCenterNavbar, + ), + ExampleItem( + builder: _titleLeftNavbar, + ), + + ExampleItem( + desc: '标题尺寸', + builder: _titleNormalNavbar, + ), + ExampleItem( + builder: _titleBelowNavbar, + ), + ExampleItem( + desc: '自定义颜色', + builder: _setBgColorNavbar, + ), + ] + ) + ], + test: [ + ExampleItem( + desc: '底部阴影', + builder: _shadowNavbar, + ), + ], + ); + } + + @Demo(group: 'navbar') + Widget _baseH5Navbar(BuildContext context) { + return const TDNavBar( + height: 48, + titleFontWeight: FontWeight.w600, + title: titleText, + screenAdaptation: false, + useDefaultBack: true, + ); + } + + @Demo(group: 'navbar') + Widget _leftMultiAction(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 16), + child: TDNavBar( + height: 48, + title: titleText, + titleFontWeight: FontWeight.w600, + screenAdaptation: false, + useDefaultBack: true, + leftBarItems: [ + TDNavBarItem(icon: TDIcons.close, iconSize: 24), + ], + rightBarItems: [ + TDNavBarItem(icon: TDIcons.ellipsis, iconSize: 24) + ] + ), + ); + } + + @Demo(group: 'navbar') + Widget _rightMultiAction(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 16), + child: TDNavBar( + height: 48, + title: titleText, + titleFontWeight: FontWeight.w600, + screenAdaptation: false, + useDefaultBack: true, + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home, iconSize: 24, ), + TDNavBarItem(icon: TDIcons.ellipsis, iconSize: 24,) + ] + ), + ); + } + + @Demo(group: 'navbar') + Widget _searchNavbar(BuildContext context){ + return TDNavBar( + useDefaultBack: false, + screenAdaptation: false, + centerTitle: false, + titleMargin: 0, + titleWidget: TDSearchBar( + needCancel: false, + autoHeight: true, + padding: const EdgeInsets.fromLTRB(0, 2, 0, 2), + placeHolder: '搜索预设文案', + mediumStyle: true, + style: TDSearchStyle.round, + onTextChanged: (String text) { + print('input:$text'); + }, + ), + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ); + } + + @Demo(group: 'navbar') + Widget _logoNavbar(BuildContext context){ + return TDNavBar( + useDefaultBack: false, + screenAdaptation: false, + centerTitle: false, + titleMargin: 0, + titleWidget: const TDImage( + assetUrl: 'assets/img/td_brand.png', + width: 102, + height: 24, + ), + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ); + } + + @Demo(group: 'navbar') + Widget _titleCenterNavbar(BuildContext context) { + return TDNavBar( + height: 48, + title: titleText, + titleFontWeight: FontWeight.w600, + screenAdaptation: false, + useDefaultBack: true, + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ); + } + + @Demo(group: 'navbar') + Widget _titleLeftNavbar(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 16), + child: TDNavBar( + height: 48, + title: titleText, + titleFontWeight: FontWeight.w600, + centerTitle: false, + titleMargin: 0, + screenAdaptation: false, + useDefaultBack: true, + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ), + ); + } + + @Demo(group: 'navbar') + Widget _titleNormalNavbar(BuildContext context) { + return TDNavBar( + height: 48, + title: titleText, + titleFontWeight: FontWeight.w600, + screenAdaptation: false, + useDefaultBack: true, + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ); + } + + @Demo(group: 'navbar') + Widget _titleBelowNavbar(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 16), + child: TDNavBar( + height: 104, + title: '返回', + titleColor: const Color.fromRGBO(0, 0, 0, 0.9), + belowTitleWidget: SizedBox( + height: 56, + child: TDText(titleText, font: Font(size: 28, lineHeight: 52), fontWeight: FontWeight.w600,), + ), + titleFont: Font(size: 16, lineHeight: 24), + centerTitle: false, + titleMargin: 0, + screenAdaptation: false, + useDefaultBack: false, + leftBarItems: [ + TDNavBarItem(icon: TDIcons.chevron_left,iconSize: 24), + ], + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home,iconSize: 24), + TDNavBarItem(icon: TDIcons.ellipsis,iconSize: 24) + ] + ), + ); + } + + @Demo(group: 'navbar') + Widget _setBgColorNavbar(BuildContext context) { + return TDNavBar( + height: 48, + title: titleText, + titleColor: Colors.white, + backgroundColor: TDTheme.of(context).brandNormalColor, + titleFontWeight: FontWeight.w600, + useDefaultBack: false, + screenAdaptation: false, + leftBarItems: [ + TDNavBarItem(icon: TDIcons.chevron_left, iconSize: 24, iconColor: Colors.white), + ], + rightBarItems: [ + TDNavBarItem(icon: TDIcons.home, iconSize: 24, iconColor: Colors.white), + TDNavBarItem(icon: TDIcons.ellipsis, iconSize: 24, iconColor: Colors.white) + ] + ); + } + + @Demo(group: 'navbar') + Widget _shadowNavbar(BuildContext context) { + return TDNavBar( + height: 48, + titleFontWeight: FontWeight.w600, + title: titleText, + screenAdaptation: false, + useDefaultBack: true, + boxShadow: [ + BoxShadow( + blurRadius: 4, + offset: const Offset(0, 4), + color: TDTheme.of(context).grayColor5, + ) + ], + ); + } +} diff --git a/tdesign-component/example/lib/page/td_notice_bar_page.dart b/tdesign-component/example/lib/page/td_notice_bar_page.dart new file mode 100644 index 000000000..770f9301a --- /dev/null +++ b/tdesign-component/example/lib/page/td_notice_bar_page.dart @@ -0,0 +1,262 @@ +/// @Type Flutter +/// @Author lwb +/// @Date 2024/5/28 + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDNoticeBarPage extends StatelessWidget { + const TDNoticeBarPage({super.key}); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(context), + exampleCodeGroup: 'noticeBar', + desc: '在导航栏下方,用于给用户显示提示消息。', + backgroundColor: Colors.white, + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '纯文字的公告栏', builder: _textNoticeBar), + ExampleItem(desc: '可滚动的公告栏', builder: _scrollNoticeBar), + ExampleItem(builder: _scrollIconNoticeBar), + ExampleItem(desc: '带图标的公告栏', builder: _iconNoticeBar), + ExampleItem(desc: '带关闭的公告栏', builder: _closeNoticeBar), + ExampleItem(desc: '带入口的公告栏', builder: _entranceNoticeBar1), + ExampleItem(builder: _entranceNoticeBar2), + ExampleItem(desc: '自定义样式的公告栏', builder: _customNoticeBar), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '普通通知', builder: _normalNoticeBar), + ExampleItem(desc: '成功通知', builder: _successNoticeBar), + ExampleItem(desc: '警示通知', builder: _warningNoticeBar), + ExampleItem(desc: '错误通知', builder: _errorNoticeBar), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem(desc: '卡片顶部', builder: _cardNoticeBar), + ]) + ], + test: [ + ExampleItem(desc: '带点击事件的公告栏', builder: _tapNoticeBar), + ExampleItem(desc: '自定义左侧内容的公告栏', builder: _leftNoticeBar), + ExampleItem(desc: '垂直滚动的公告栏', builder: _stepNoticeBar), + ], + ); + } +} + +@Demo(group: 'noticeBar') +Widget _textNoticeBar(BuildContext context) { + return const TDNoticeBar(context: '这是一条普通的通知信息'); +} + +@Demo(group: 'noticeBar') +Widget _scrollNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '提示文字描述提示文字描述提示文字描述提示文字描述提示文字', + marquee: true, + speed: 50, + ); +} + +@Demo(group: 'noticeBar') +Widget _scrollIconNoticeBar(BuildContext context) { + return const Padding( + padding: EdgeInsets.only(top: 16), + child: TDNoticeBar( + context: '提示文字描述提示文字描述提示文字描述提示文字描述提示文字', + speed: 50, + prefixIcon: TDIcons.sound, + marquee: true, + ), + ); +} + +@Demo(group: 'noticeBar') +Widget _iconNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + ); +} + +@Demo(group: 'noticeBar') +Widget _closeNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.close, + ); +} + +@Demo(group: 'noticeBar') +Widget _entranceNoticeBar1(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + right: TDButton( + text: '文字按钮', + type: TDButtonType.text, + theme: TDButtonTheme.primary, + size: TDButtonSize.extraSmall, + height: 22, + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0), + ), + ); +} + +@Demo(group: 'noticeBar') +Widget _entranceNoticeBar2(BuildContext context) { + return const Padding( + padding: EdgeInsets.only(top: 16), + child: TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.chevron_right, + ), + ); +} + +@Demo(group: 'noticeBar') +Widget _customNoticeBar(BuildContext context) { + return TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.notification, + suffixIcon: TDIcons.chevron_right, + style: TDNoticeBarStyle(backgroundColor: TDTheme.of(context).grayColor3), + ); +} + +@Demo(group: 'noticeBar') +Widget _normalNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.info, + ); +} + +@Demo(group: 'noticeBar') +Widget _successNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.success, + ); +} + +@Demo(group: 'noticeBar') +Widget _warningNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.warning, + ); +} + +@Demo(group: 'noticeBar') +Widget _errorNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + theme: TDNoticeBarTheme.error, + ); +} + +@Demo(group: 'noticeBar') +Widget _cardNoticeBar(BuildContext context) { + var size = MediaQuery.of(context).size; + return Container( + margin: const EdgeInsets.symmetric(horizontal: 16), + decoration: BoxDecoration( + color: TDNoticeBarStyle.generateTheme(context).backgroundColor, + borderRadius: const BorderRadius.all(Radius.circular(9)), + boxShadow: const [ + BoxShadow( + color: Color(0x0d000000), + blurRadius: 8, + spreadRadius: 2, + offset: Offset(0, 2), + ), + BoxShadow( + color: Color(0x0f000000), + blurRadius: 10, + spreadRadius: 1, + offset: Offset(0, 8), + ), + BoxShadow( + color: Color(0x1a000000), + blurRadius: 5, + spreadRadius: -3, + offset: Offset(0, 5), + ), + ], + ), + child: Column( + children: [ + Container( + width: size.width - 32, + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + clipBehavior: Clip.hardEdge, + child: const TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.chevron_right, + ), + ), + Container( + height: 150, + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.all(Radius.circular(12)), + ), + ) + ], + ), + ); +} + +@Demo(group: 'noticeBar') +Widget _tapNoticeBar(BuildContext context) { + return TDNoticeBar( + context: '这是一条普通的通知信息', + prefixIcon: TDIcons.error_circle_filled, + suffixIcon: TDIcons.chevron_right, + onTap: (trigger) { + TDToast.showText('tap:$trigger', context: context); + }, + ); +} + +@Demo(group: 'noticeBar') +Widget _leftNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: '这是一条普通的通知信息', + suffixIcon: TDIcons.chevron_right, + left: TDButton( + text: '文本', + type: TDButtonType.text, + theme: TDButtonTheme.primary, + size: TDButtonSize.extraSmall, + height: 22, + padding: EdgeInsets.symmetric(vertical: 0, horizontal: 0), + ), + ); +} + +@Demo(group: 'noticeBar') +Widget _stepNoticeBar(BuildContext context) { + return const TDNoticeBar( + context: ['君不见黄河之水天上来', '奔流到海不复回', '君不见'], + direction: Axis.vertical, + prefixIcon: TDIcons.sound, + marquee: true, + ); +} diff --git a/tdesign-component/example/lib/page/td_picker_page.dart b/tdesign-component/example/lib/page/td_picker_page.dart new file mode 100644 index 000000000..b0219ed80 --- /dev/null +++ b/tdesign-component/example/lib/page/td_picker_page.dart @@ -0,0 +1,269 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + + +class TDPickerPage extends StatefulWidget { + const TDPickerPage({Key? key}) : super(key: key); + + @override + State createState() => _TDPickerPageState(); +} + +class _TDPickerPageState extends State { + String selected_1 = ''; + List data_1 = ['广州市', '韶关市', '深圳市', '珠海区', '汕头市']; + String selected_2 = ''; + String selected_3 = ''; + List> data_2 = []; + String selected_4 = ''; + Map data_3 = { + '广东省': { + '深圳市': ['南山区南山区南山区南山区南山区', '宝安区', '罗湖区', '福田区'], + '佛山市': [''], + '广州市广州市广州市广州市广州市广州市广州市广州市广州市广州市广州市': ['花都区'] + }, + '广东省2': { + '深圳市': ['南山区南山区南山区南山区南山区', '罗湖区', '福田区'], + '广州市广州市广州市广州市广州市广州市广州市广州市广州市广州市广州市': ['花都区'] + }, + '重庆市': { + '重庆市重庆市重庆市重庆市重庆市重庆市重庆市': ['九龙坡区', '江北区'], + }, + '浙江省浙江省浙江省浙江省浙江省浙江省浙江省浙江省': { + '杭州市': ['西湖区', '余杭区', '萧山区'], + '宁波市': ['江东区', '北仑区', '奉化市'] + }, + '香港': { + '香港': ['九龙城区', '黄大仙区', '离岛区', '湾仔区'] + }, + }; + + String selected_5 = ''; + + @override + void initState() { + var list = []; + for(var i = 2022; i >= 2000; i--) { + list.add('${i}年'); + } + data_2.add(list); + data_2.add(['春', '夏', '秋', '冬']); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '用于一组预设数据中的选择。', + exampleCodeGroup: 'picker', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '基础选择器--地区', builder: buildArea), + ExampleItem(desc: '基础选择器--时间', builder: buildTime), + ExampleItem(desc: '基础选择器--地区--联动', builder: buildMultiArea), + ], + ), + ExampleModule( + title: '组件样式', + children: [ + ExampleItem(desc: '带标题选择器', builder: buildAreaWithTitle), + ExampleItem(desc: '无标题选择器', builder: buildAreaWithoutTitle), + ], + ) + ], + test: [ + ExampleItem( + desc: '自定义left/right text', builder: buildCustomLeftRightText), + ExampleItem( + desc: '级联选择保持下一级选项', builder: buildKeepMultiArea), + ], + ); + } + + @Demo(group: 'picker') + Widget buildArea(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiPicker(context, title: '选择地区', + onConfirm: (selected) { + setState(() { + selected_1 = '${data_1[selected[0]]}'; + }); + Navigator.of(context).pop(); + }, data: [data_1]); + }, + child: buildSelectRow(context, selected_1, '选择地区'), + ); + } + + @Demo(group: 'picker') + Widget buildTime(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiPicker(context, title: '选择时间', + onConfirm: (selected) { + setState(() { + selected_2 = '${data_2[0][selected[0]]} ${data_2[1][selected[1]]}'; + }); + Navigator.of(context).pop(); + }, data: data_2); + }, + child: buildSelectRow(context, selected_2, '选择时间'), + ); + } + + @Demo(group: 'picker') + Widget buildMultiArea(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiLinkedPicker(context, title: '选择地区', + onConfirm: (selected) { + setState(() { + selected_3 = '${selected[0]} ${selected[1]} ${selected[2]}'; + }); + Navigator.of(context).pop(); + }, + data: data_3, + columnNum: 3, + initialData: ['浙江省', '杭州市', '西湖区']); + }, + child: buildSelectRow(context, selected_3, '选择地区'), + ); + } + + @Demo(group: 'picker') + Widget buildAreaWithTitle(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiPicker(context, title: '选择地区', + onConfirm: (selected) { + setState(() { + selected_4 = '${data_1[selected[0]]}'; + }); + Navigator.of(context).pop(); + }, data: [data_1]); + }, + child: buildSelectRow(context, selected_4, '带标题选择器'), + ); + } + + @Demo(group: 'picker') + Widget buildAreaWithoutTitle(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiPicker(context, title: '', + onConfirm: (selected) { + setState(() { + selected_5 = '${data_1[selected[0]]}'; + }); + Navigator.of(context).pop(); + }, data: [data_1]); + }, + child: buildSelectRow(context, selected_5, '无标题选择器'), + ); + } + + @Demo(group: 'picker') + Widget buildCustomLeftRightText(BuildContext context) { + return Column( + children: [ + GestureDetector( + onTap: () { + TDPicker.showMultiPicker(context, + leftText: '自定义取消', + rightText: '自定义确认', + title: '基础选择器', onConfirm: (selected) { + setState(() { + selected_5 = '${data_1[selected[0]]}'; + }); + Navigator.of(context).pop(); + }, data: [data_1]); + }, + child: buildSelectRow(context, selected_5, '基础选择器'), + ), + GestureDetector( + onTap: () { + TDPicker.showMultiLinkedPicker(context, + leftText: '自定义取消', + rightText: '自定义确认', + title: '联动选择器', onConfirm: (selected) { + setState(() { + selected_3 = '${selected[0]} ${selected[1]} ${selected[2]}'; + }); + Navigator.of(context).pop(); + }, data: data_3, columnNum: 3, initialData: ['浙江省', '杭州市', '西湖区']); + }, + child: buildSelectRow(context, selected_3, '联动选择器'), + ) + ], + ); + } + + @Demo(group: 'picker') + Widget buildKeepMultiArea(BuildContext context) { + return GestureDetector( + onTap: (){ + TDPicker.showMultiLinkedPicker(context, title: '选择地区', + onConfirm: (selected) { + setState(() { + selected_3 = '${selected[0]} ${selected[1]} ${selected[2]}'; + }); + Navigator.of(context).pop(); + }, + data: data_3, + columnNum: 3, + keepSameSelection: true, + initialData: ['广东省', '深圳市', '罗湖区']); + }, + child: buildSelectRow(context, selected_3, '选择地区'), + ); + } + + Widget buildSelectRow(BuildContext context, String output, String title) { + return Container( + color: TDTheme.of(context).whiteColor1, + height: 56, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, top: 16, bottom: 16), + child: TDText(title, font: TDTheme.of(context).fontBodyLarge,), + ), + Expanded(child: Padding( + padding: const EdgeInsets.only(right: 16, left: 16), + child: Row( + children: [ + Expanded(child: TDText( + output, + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).fontGyColor3.withOpacity(0.4), + maxLines: 1, + overflow: TextOverflow.ellipsis, + )), + Padding( + padding: const EdgeInsets.only(left: 2), + child: Icon( + TDIcons.chevron_right, + color: TDTheme.of(context).fontGyColor3.withOpacity(0.4),), + ), + ], + ), + )), + ], + ), + const TDDivider(margin: EdgeInsets.only(left: 16, ),) + ], + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_popover_page.dart b/tdesign-component/example/lib/page/td_popover_page.dart new file mode 100644 index 000000000..1c2f463fd --- /dev/null +++ b/tdesign-component/example/lib/page/td_popover_page.dart @@ -0,0 +1,781 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDPopoverPage extends StatefulWidget { + const TDPopoverPage({super.key}); + + @override + State createState() => _TDPopoverPage(); +} + +class _TDPopoverPage extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '用于文字提示的气泡框。', + exampleCodeGroup: 'popover', + backgroundColor: TDTheme.of(context).whiteColor1, + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '带箭头的弹出气泡', builder: _buildPopover), + ExampleItem(desc: '不带箭头的弹出气泡', builder: _buildNoArrowPopover), + ExampleItem(desc: '自定义内容弹出气泡', builder: _buildNCustomPopover), + ], + ), + ExampleModule(title: '组件样式', children: [ + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Column( + children: [ + Flex( + direction: Axis.horizontal, + children: [ + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildDarkPopover), + ), + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildLightPopover), + ), + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildInfoPopover), + ), + ], + ), + Flex( + direction: Axis.horizontal, + children: [ + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildSuccessPopover), + ), + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildWarningPopover), + ), + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildErrorPopover), + ), + ], + ) + ], + ), + ); + }, + ), + ExampleItem( + desc: '顶部弹出气泡', + ignoreCode: true, + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Flex( + direction: Axis.horizontal, + children: [ + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildTopLeftPopover), + ), + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildTopPopover), + ), + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildTopRightPopover), + ), + ], + ), + ); + }, + ), + ExampleItem( + desc: '底部弹出气泡', + ignoreCode: true, + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Flex( + direction: Axis.horizontal, + children: [ + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildBottomLeftPopover), + ), + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildBottomPopover), + ), + Expanded( + flex: 1, + child: CodeWrapper(builder: _buildBottomRightPopover), + ), + ], + ), + ); + }, + ), + ExampleItem( + desc: '右侧弹出气泡', + ignoreCode: true, + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Flex( + direction: Axis.horizontal, + children: [ + Expanded( + flex: 1, + child: Column( + children: [ + CodeWrapper(builder: _buildRightTopPopover), + CodeWrapper(builder: _buildRightPopover), + CodeWrapper(builder: _buildRightBottomPopover), + ], + ), + ), + const Expanded( + flex: 1, + child: SizedBox(), + ), + ], + ), + ); + }, + ), + ExampleItem( + desc: '左侧弹出气泡', + ignoreCode: true, + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Flex( + direction: Axis.horizontal, + children: [ + const Expanded( + flex: 1, + child: SizedBox(), + ), + Expanded( + flex: 1, + child: Column( + children: [ + CodeWrapper(builder: _buildLeftTopPopover), + CodeWrapper(builder: _buildLeftPopover), + CodeWrapper(builder: _buildLeftBottomPopover), + ], + ), + ), + ], + ), + ); + }, + ), + ]) + ], + test: [ + ExampleItem( + desc: '显示多行内容', + builder: _buildMultiLinePopover, + ) + ], + ); + } + + @Demo(group: 'popover') + Widget _buildPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '带箭头', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover(context: _, content: '弹出气泡内容'); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildNoArrowPopover(BuildContext context) { + return LayoutBuilder( + builder: (_, constrains) { + return TDButton( + size: TDButtonSize.medium, + text: '不带箭头', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, content: '弹出气泡内容', showArrow: false); + }, + ); + }, + ); + } + + Widget _buildPopoverList(BuildContext context) { + return Column( + children: [ + Container( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24), + child: TDText('选项1', + style: TextStyle(color: TDTheme.of(context).whiteColor1)), + ), + TDDivider(color: TDTheme.of(context).whiteColor1, height: 0.5), + Container( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24), + child: TDText('选项2', + style: TextStyle(color: TDTheme.of(context).whiteColor1)), + ), + TDDivider(color: TDTheme.of(context).whiteColor1, height: 0.5), + Container( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24), + child: TDText('选项3', + style: TextStyle(color: TDTheme.of(context).whiteColor1)), + ), + ], + ); + } + + @Demo(group: 'popover') + Widget _buildNCustomPopover(BuildContext context) { + return LayoutBuilder( + builder: (_, constrains) { + return TDButton( + text: '自定义内容', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + padding: const EdgeInsets.all(0), + width: 108, + height: 148, + contentWidget: _buildPopoverList(context), + ); + }, + ); + }, + ); + } + + @Demo(group: 'popover') + Widget _buildDarkPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '深色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildLightPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '浅色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + theme: TDPopoverTheme.light, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildInfoPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '品牌色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + theme: TDPopoverTheme.info, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildSuccessPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '成功色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + theme: TDPopoverTheme.success, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildWarningPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '警告色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + theme: TDPopoverTheme.warning, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildErrorPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '错误色', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + theme: TDPopoverTheme.error, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildTopLeftPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '顶部左', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.topLeft, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildTopPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '顶部中', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.top, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildTopRightPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '顶部右', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.topRight, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildBottomLeftPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '底部左', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.bottomLeft, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildBottomPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '底部中', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.bottom, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildBottomRightPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '底部右', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.bottomRight, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildRightTopPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '右侧上', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.rightTop, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildRightPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '右侧中', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.right, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildRightBottomPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '右侧下', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.rightBottom, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildLeftTopPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '左侧上', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.leftTop, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildLeftPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '左侧中', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.left, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildLeftBottomPopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '左侧下', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + content: '弹出气泡内容', + placement: TDPopoverPlacement.leftBottom, + ); + }, + ); + }, + ), + ); + } + + @Demo(group: 'popover') + Widget _buildMultiLinePopover(BuildContext context) { + return Container( + padding: const EdgeInsets.only(top: 0), + margin: const EdgeInsets.all(8), + child: LayoutBuilder( + builder: (_, constraints) { + return TDButton( + size: TDButtonSize.medium, + text: '多行内容', + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + + onTap: () { + TDPopover.showPopover( + context: _, + width: 200, + content: '弹出气泡内容弹出气泡内容弹出气泡内容弹出气泡内容', + ); + }, + ); + }, + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_popup_page.dart b/tdesign-component/example/lib/page/td_popup_page.dart new file mode 100644 index 000000000..13ed6a17f --- /dev/null +++ b/tdesign-component/example/lib/page/td_popup_page.dart @@ -0,0 +1,854 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +/// +/// TDPopup演示 +/// +class TDPopupPage extends StatefulWidget { + const TDPopupPage({Key? key}) : super(key: key); + + @override + State createState() { + return TDPopupPageState(); + } +} + +class TDPopupPageState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + padding: const EdgeInsets.only(top: 16), + backgroundColor: Colors.white, + exampleCodeGroup: 'popup', + desc: '由其他控件触发,屏幕滑出或弹出一块自定义内容区域', + navBarKey: navBarkey, + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(builder: _buildPopFromTop), + ExampleItem(builder: _buildPopFromLeft), + ExampleItem(builder: _buildPopFromCenter), + ExampleItem(builder: _buildPopFromBottom), + ExampleItem(builder: _buildPopFromRight), + ], + ), + ExampleModule( + title: '组件示例', + children: [ + ExampleItem(builder: _buildPopFromBottomWithOperationAndTitle), + ExampleItem(builder: _buildPopFromBottomWithOperation), + ExampleItem(builder: _buildPopFromBottomWithCloseAndTitle), + ExampleItem(builder: _buildPopFromBottomWithCloseAndLeftTitle), + ExampleItem(builder: _buildPopFromBottomWithClose), + ExampleItem(builder: _buildPopFromBottomWithTitle), + ExampleItem(builder: _buildPopFromCenterWithClose), + ExampleItem(builder: _buildPopFromCenterWithUnderClose), + ], + ), + ], + test: [ + ExampleItem( + desc: '操作栏超长文本,指定颜色', + builder: (_) { + return TDButton( + text: '底部弹出层-带标题及操作', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomConfirmPanel( + title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字标题文字', + leftText: '点这里确认!', + leftTextColor: TDTheme.of(context).brandNormalColor, + leftClick: () { + TDToast.showText('确认', context: context); + Navigator.maybePop(context); + }, + rightText: '关闭', + rightTextColor: TDTheme.of(context).errorNormalColor, + rightClick: () { + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + }), + ExampleItem( + desc: '带关闭超长文本', + builder: (_) { + return TDButton( + text: '底部弹出层-带标题及操作', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', + closeColor: TDTheme.of(context).errorNormalColor, + closeClick: () { + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + }), + ExampleItem( + desc: '修改圆角', + builder: (_) { + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Container( + margin: const EdgeInsets.all(8), + child: TDButton( + text: '底部弹出层-修改圆角', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', + closeColor: TDTheme.of(context).errorNormalColor, + closeClick: () { + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + radius: 6, + ); + })); + }, + ), + ), + Container( + margin: const EdgeInsets.all(8), + child: TDButton( + text: '底部弹出层-修改圆角', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomConfirmPanel( + title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', + leftText: '点这里确认!', + leftTextColor: TDTheme.of(context).brandNormalColor, + leftClick: () { + TDToast.showText('确认', context: context); + Navigator.maybePop(context); + }, + rightText: '关闭', + rightTextColor: TDTheme.of(context).errorNormalColor, + rightClick: () { + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + radius: 6, + ); + })); + }, + )), + Container( + margin: const EdgeInsets.all(8), + child: TDButton( + text: '居中弹出层-修改圆角', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.center, + builder: (context) { + return TDPopupCenterPanel( + closeColor: TDTheme.of(context).errorNormalColor, + closeClick: () { + Navigator.maybePop(context); + }, + child: const SizedBox( + height: 240, + width: 240, + ), + radius: 6, + ); + })); + }, + )), + Container( + margin: const EdgeInsets.all(8), + child: TDButton( + text: '居中弹出层-底部关闭-修改圆角', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.center, + builder: (context) { + return TDPopupCenterPanel( + closeUnderBottom: true, + closeClick: () { + Navigator.maybePop(context); + }, + child: const SizedBox( + height: 240, + width: 240, + ), + radius: 6, + ); + })); + }, + )), + ], + ); + }), + ExampleItem( + desc: '自定义位置', + builder: (_) { + return TDButton( + text: '自定义位置', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + var renderBox = navBarkey.currentContext!.findRenderObject() as RenderBox; + Navigator.of(context).push( + TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.right, + modalTop: renderBox.size.height, + builder: (context) { + return Container( + color: Colors.white, + width: 280, + ); + }, + ), + ); + }, + ); + }, + ), + ExampleItem( + desc: '弹出层包含输入框且不会被键盘遮挡', + builder: (_) { + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Container( + margin: const EdgeInsets.all(8), + child: TDButton( + text: '底部弹出层-键盘弹默认遮挡', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', + closeColor: TDTheme.of(context).errorNormalColor, + closeClick: () { + Navigator.maybePop(context); + }, + child: Material( + child: SizedBox( + height: 100, + child: TDInput( + type: TDInputType.normal, + leftLabel: '标签文字', + hintText: '请输入文字', + maxLength: 10, + additionInfo: '最大输入10个字符', + backgroundColor: Colors.white, + ), + ), + ), + radius: 6, + ); + })); + }, + ), + ), + Container( + margin: const EdgeInsets.all(8), + child: TDButton( + text: '底部弹出层-键盘弹出不遮挡', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + focusMove: true, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: '标题文字标题文字标题文字标题文字标题文字标题文字标题文字', + closeColor: TDTheme.of(context).errorNormalColor, + closeClick: () { + Navigator.maybePop(context); + }, + child: Material( + child: SizedBox( + height: 100, + child: TDInput( + type: TDInputType.normal, + leftLabel: '标签文字', + hintText: '请输入文字', + maxLength: 10, + additionInfo: '最大输入10个字符', + backgroundColor: Colors.white, + ), + ), + ), + radius: 6, + ); + })); + }, + ), + ), + Container( + margin: const EdgeInsets.all(8), + child: TDButton( + text: '居中弹出层-键盘弹出不遮挡', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.center, + focusMove: true, + builder: (context) { + return TDPopupCenterPanel( + closeColor: TDTheme.of(context).errorNormalColor, + closeClick: () { + Navigator.maybePop(context); + }, + child: Material( + child: SizedBox( + height: 340, + child: Column( + children: [ + TDInput( + type: TDInputType.normal, + leftLabel: '标签文字1', + hintText: '请输入文字1', + maxLength: 10, + backgroundColor: Colors.white, + ), + TDInput( + type: TDInputType.normal, + leftLabel: '标签文字2', + hintText: '请输入文字2', + maxLength: 10, + backgroundColor: Colors.white, + ), + TDInput( + type: TDInputType.normal, + leftLabel: '标签文字3', + hintText: '请输入文字3', + maxLength: 10, + backgroundColor: Colors.white, + ), + TDInput( + type: TDInputType.normal, + leftLabel: '标签文字4', + hintText: '请输入文字4', + maxLength: 10, + backgroundColor: Colors.white, + ), + TDInput( + type: TDInputType.normal, + leftLabel: '会被键盘遮挡的输入框1', + hintText: '会被键盘遮挡小部分', + maxLength: 10, + backgroundColor: Colors.white, + ), + TDInput( + type: TDInputType.normal, + leftLabel: '会被键盘遮挡的输入框2', + hintText: '会被键盘遮挡全遮挡', + maxLength: 10, + backgroundColor: Colors.white, + ) + ], + ), + ), + ), + radius: 6, + ); + })); + }, + )) + ], + ); + }), + ExampleItem( + desc: '可拖动全屏', + builder: (_) { + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Container( + margin: const EdgeInsets.all(8), + child: TDButton( + text: '可拖动全屏', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: '标题文字', + draggable: true, + closeColor: TDTheme.of(context).errorNormalColor, + closeClick: () { + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + )), + Container( + margin: const EdgeInsets.all(8), + child: TDButton( + text: '可拖动全屏-带标题及操作', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomConfirmPanel( + title: '标题文字', + draggable: true, + leftClick: () { + Navigator.maybePop(context); + }, + rightClick: () { + TDToast.showText('确定', context: context); + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + )), + ]); + }, + ), + ], + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromTop(BuildContext context) { + return TDButton( + text: '顶部弹出', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.top, + open: () { + print('open'); + }, + opened: () { + print('opened'); + }, + builder: (context) { + return Container( + color: Colors.white, + height: 240, + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromLeft(BuildContext context) { + return TDButton( + text: '左侧弹出', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.left, + builder: (context) { + return Container( + color: Colors.white, + width: 280, + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromCenter(BuildContext context) { + return TDButton( + text: '中间弹出', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.center, + builder: (context) { + return Container( + color: Colors.white, + width: 240, + height: 240, + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromBottom(BuildContext context) { + return TDButton( + text: '底部弹出', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return Container( + color: Colors.white, + height: 240, + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromRight(BuildContext context) { + return TDButton( + text: '右侧弹出', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.right, + builder: (context) { + return Container( + color: Colors.white, + width: 280, + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromBottomWithOperationAndTitle(BuildContext context) { + return TDButton( + text: '底部弹出层-带标题及操作', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomConfirmPanel( + title: '标题文字', + leftClick: () { + Navigator.maybePop(context); + }, + rightClick: () { + TDToast.showText('确定', context: context); + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromBottomWithOperation(BuildContext context) { + return TDButton( + text: '底部弹出层-带操作', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomConfirmPanel( + leftClick: () { + Navigator.maybePop(context); + }, + rightClick: () { + TDToast.showText('确定', context: context); + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromBottomWithCloseAndTitle(BuildContext context) { + return TDButton( + text: '底部弹出层-带标题及关闭', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: '标题文字', + closeClick: () { + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromBottomWithCloseAndLeftTitle(BuildContext context) { + return TDButton( + text: '底部弹出层-带左边标题及关闭', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: '标题文字', + titleLeft: true, + closeClick: () { + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromBottomWithClose(BuildContext context) { + return TDButton( + text: '底部弹出层-带关闭', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + closeClick: () { + Navigator.maybePop(context); + }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromBottomWithTitle(BuildContext context) { + return TDButton( + text: '底部弹出层-仅标题', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + slideTransitionFrom: SlideTransitionFrom.bottom, + builder: (context) { + return TDPopupBottomDisplayPanel( + title: '标题文字', + hideClose: true, + // closeClick: () { + // Navigator.maybePop(context); + // }, + child: Container( + height: 200, + ), + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromCenterWithClose(BuildContext context) { + return TDButton( + text: '居中弹出层-带关闭', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + isDismissible: false, + slideTransitionFrom: SlideTransitionFrom.center, + builder: (context) { + return TDPopupCenterPanel( + closeClick: () { + Navigator.maybePop(context); + }, + child: const SizedBox( + width: 240, + height: 240, + ), + ); + })); + }, + ); + } + + @Demo(group: 'popup') + Widget _buildPopFromCenterWithUnderClose(BuildContext context) { + return TDButton( + text: '居中弹出层-关闭在下方', + isBlock: true, + theme: TDButtonTheme.primary, + type: TDButtonType.outline, + size: TDButtonSize.large, + onTap: () { + Navigator.of(context).push(TDSlidePopupRoute( + modalBarrierColor: TDTheme.of(context).fontGyColor2, + isDismissible: false, + slideTransitionFrom: SlideTransitionFrom.center, + builder: (context) { + return TDPopupCenterPanel( + closeUnderBottom: true, + closeClick: () { + Navigator.maybePop(context); + }, + child: const SizedBox( + width: 240, + height: 240, + ), + ); + })); + }, + ); + } +} diff --git a/tdesign-component/example/lib/page/td_progress_page.dart b/tdesign-component/example/lib/page/td_progress_page.dart new file mode 100644 index 000000000..2737b6e58 --- /dev/null +++ b/tdesign-component/example/lib/page/td_progress_page.dart @@ -0,0 +1,272 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDProgressPage extends StatefulWidget { + const TDProgressPage({Key? key}) : super(key: key); + + final examplePadding = const EdgeInsets.symmetric(horizontal: 16); + + @override + State createState() { + return _TDProgressPageState(); + } +} + +class _TDProgressPageState extends State { + TDLabelWidget buttonLabel = const TDTextLabel('开始'); + double progressValue = 0.0; + Timer? _timer; + bool isProgressing = false; + bool isPlaying = false; + double microProgressValue = 0.3; + Timer? _microTimer; + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '用于展示任务当前的进度', + exampleCodeGroup: 'progress', + backgroundColor: TDTheme.of().whiteColor1, + padding: const EdgeInsets.symmetric(vertical: 8), + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + desc: '线性进度条', + padding: widget.examplePadding, + builder: _buildRightLabelLinear), + ExampleItem( + desc: '百分比内显', + padding: widget.examplePadding, + builder: _buildInsideLabelLinear), + ExampleItem( + desc: '环形进度条', + padding: widget.examplePadding, + center: false, + builder: _buildCircle), + ExampleItem( + desc: '微型环形进度条', + padding: widget.examplePadding, + center: false, + builder: _buildMicro), + ExampleItem( + desc: '按钮进度条', + padding: widget.examplePadding, + builder: _buildButton), + ExampleItem( + desc: '微型按钮进度条', + padding: widget.examplePadding, + center: false, + builder: _buildMicroButton), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem( + desc: '线性进度条', + padding: widget.examplePadding, + builder: _buildPrimary), + ExampleItem(padding: widget.examplePadding, builder: _buildWarning), + ExampleItem(padding: widget.examplePadding, builder: _buildDanger), + ExampleItem(padding: widget.examplePadding, builder: _buildSuccess), + ExampleItem( + desc: '环形进度条', + padding: widget.examplePadding, + center: false, + builder: _buildCirclePrimary), + ExampleItem( + padding: widget.examplePadding, + center: false, + builder: _buildCircleWarning), + ExampleItem( + padding: widget.examplePadding, + center: false, + builder: _buildCircleDanger), + ExampleItem( + padding: widget.examplePadding, + center: false, + builder: _buildCircleSuccess), + ]) + ]); + } + + @Demo(group: 'progress') + Widget _buildRightLabelLinear(BuildContext context) { + return TDProgress( + type: TDProgressType.linear, + value: 0.8, + strokeWidth: 6, + progressLabelPosition: TDProgressLabelPosition.right); + } + + @Demo(group: 'progress') + Widget _buildInsideLabelLinear(BuildContext context) { + return TDProgress(type: TDProgressType.linear, value: 0.8); + } + + @Demo(group: 'progress') + Widget _buildCircle(BuildContext context) { + return TDProgress(type: TDProgressType.circular, value: 0.3); + } + + @Demo(group: 'progress') + Widget _buildMicro(BuildContext context) { + return TDProgress(type: TDProgressType.micro, value: 0.75); + } + + @Demo(group: 'progress') + Widget _buildButton(BuildContext context) { + return TDProgress( + type: TDProgressType.button, + onTap: _toggleProgress, + onLongPress: _resetProgress, + value: progressValue, + label: buttonLabel); + } + + @Demo(group: 'progress') + Widget _buildMicroButton(BuildContext context) { + return TDProgress( + type: TDProgressType.micro, + value: microProgressValue, + onTap: _toggleMicroProgress, + label: TDIconLabel(isPlaying ? Icons.pause : Icons.play_arrow, + color: TDTheme.of(context).brandNormalColor), + ); + } + + @Demo(group: 'progress') + Widget _buildPrimary(BuildContext context) { + return TDProgress( + type: TDProgressType.linear, + progressStatus: TDProgressStatus.primary, + value: 0.8, + strokeWidth: 6, + progressLabelPosition: TDProgressLabelPosition.right); + } + + @Demo(group: 'progress') + Widget _buildWarning(BuildContext context) { + return TDProgress( + type: TDProgressType.linear, + progressStatus: TDProgressStatus.warning, + value: 0.8, + strokeWidth: 6, + progressLabelPosition: TDProgressLabelPosition.right, + ); + } + + @Demo(group: 'progress') + Widget _buildDanger(BuildContext context) { + return TDProgress( + type: TDProgressType.linear, + progressStatus: TDProgressStatus.danger, + value: 0.8, + strokeWidth: 6, + progressLabelPosition: TDProgressLabelPosition.right); + } + + @Demo(group: 'progress') + Widget _buildSuccess(BuildContext context) { + return TDProgress( + type: TDProgressType.linear, + progressStatus: TDProgressStatus.success, + value: 0.8, + strokeWidth: 6, + progressLabelPosition: TDProgressLabelPosition.right); + } + + @Demo(group: 'progress') + Widget _buildCirclePrimary(BuildContext context) { + return TDProgress( + type: TDProgressType.circular, + progressStatus: TDProgressStatus.primary, + value: 0.3); + } + + @Demo(group: 'progress') + Widget _buildCircleWarning(BuildContext context) { + return TDProgress( + type: TDProgressType.circular, + progressStatus: TDProgressStatus.warning, + value: 0.3); + } + + @Demo(group: 'progress') + Widget _buildCircleDanger(BuildContext context) { + return TDProgress( + type: TDProgressType.circular, + progressStatus: TDProgressStatus.danger, + value: 0.3); + } + + @Demo(group: 'progress') + Widget _buildCircleSuccess(BuildContext context) { + return TDProgress( + type: TDProgressType.circular, + progressStatus: TDProgressStatus.success, + value: 1); + } + + void _toggleProgress() { + if (isProgressing) { + // 暂停进度 + _timer?.cancel(); + setState(() { + buttonLabel = const TDTextLabel('继续'); + isProgressing = false; + }); + } else { + // 开始或继续进度 + _timer?.cancel(); + _timer = Timer.periodic(const Duration(milliseconds: 100), (timer) { + setState(() { + if (progressValue < 1.0) { + progressValue += 0.01; + buttonLabel = TDTextLabel('${(progressValue * 100).toInt()}%'); + } else { + _timer?.cancel(); + buttonLabel = const TDTextLabel('完成'); + isProgressing = false; + } + }); + }); + setState(() { + isProgressing = true; + }); + } + } + + void _resetProgress() { + _timer?.cancel(); + setState(() { + progressValue = 0.0; + buttonLabel = const TDTextLabel('开始'); + isProgressing = false; + }); + } + + void _toggleMicroProgress() { + setState(() { + isPlaying = !isPlaying; + }); + if (isPlaying) { + _microTimer = Timer.periodic(const Duration(milliseconds: 100), (timer) { + setState(() { + if (microProgressValue < 1.0) { + microProgressValue += 0.01; + } else { + _microTimer?.cancel(); + isPlaying = false; + microProgressValue = 0.0; + } + }); + }); + } else { + _microTimer?.cancel(); + } + } +} diff --git a/tdesign-component/example/lib/page/td_radio_page.dart b/tdesign-component/example/lib/page/td_radio_page.dart new file mode 100644 index 000000000..24455a2e7 --- /dev/null +++ b/tdesign-component/example/lib/page/td_radio_page.dart @@ -0,0 +1,468 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +/// +/// TDRadio演示 +/// +class TDRadioPage extends StatefulWidget { + const TDRadioPage({Key? key}) : super(key: key); + + @override + State createState() { + return TDRadioPageState(); + } +} + +class TDRadioPageState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + exampleCodeGroup: 'radio', + backgroundColor: const Color(0xfff6f6f6), + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '纵向单选框', builder: _verticalRadios), + ExampleItem(desc: '横向单选框', builder: _horizontalRadios), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '单选框状态', builder: _radioStatus), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem(desc: '勾选样式', builder: _checkStyle), + ExampleItem(desc: '勾选显示位置', builder: _checkPosition), + ExampleItem(desc: '非通栏单选样式', builder: _passThroughStyle), + ]), + ExampleModule(title: '特殊样式', children: [ + ExampleItem(desc: '纵向卡片单选框', builder: _verticalCardStyle), + ExampleItem(desc: '横向卡片单选框', builder: _horizontalCardStyle), + ]), + ], + test: [ + ExampleItem(desc: '横向单选框-显示下划线', builder: _showBottomLine), + ExampleItem(desc: '横向单选框-自定义下划线', builder: _customBottomLine), + ExampleItem(desc: '横向单选框-自定义颜色和字体尺寸', builder: _customColorAndFont), + ExampleItem(desc: '横向单选框-自定义禁用字体颜色', builder: _customDisableColorAndFont), + ExampleItem(desc: '横向单选框-自定义选框左侧间距', builder: _customRadioLeftSpace), + ], + ); + } + + @Demo(group: 'radio') + Widget _verticalRadios(BuildContext context) { + return TDCell( + title: '单选标题', + hover: false, + required: true, + descriptionWidget: TDRadioGroup( + selectId: '0', + direction: Axis.horizontal, + directionalTdRadios: const [TDRadio( + id: '0', + title: '单选标题0', + showDivider: false, + ), + TDRadio( + id: '1', + title: '单选标题1', + showDivider: false, + ), + ], + ), + ); + } + + @Demo(group: 'radio') + Widget _horizontalRadios(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + direction: Axis.horizontal, + directionalTdRadios: const [ + TDRadio( + id: '0', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '1', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '2', + title: '上限四字', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + ], + ); + } + + @Demo(group: 'radio') + Widget _radioStatus(BuildContext context) { + return TDRadioGroup( + contentDirection: TDContentDirection.right, + selectId: '0', + child: const Column( + children: [ + TDRadio( + id: '0', + title: '选项禁用-已选', + radioStyle: TDRadioStyle.circle, + enable: false, + ), + TDRadio( + id: '1', + title: '选项禁用-默认', + radioStyle: TDRadioStyle.circle, + enable: false, + ), + ], + ), + ); + } + + @Demo(group: 'radio') + Widget _checkStyle(BuildContext context) { + return Column( + children: [ + TDRadioGroup( + radioCheckStyle: TDRadioStyle.check, + selectId: 'index:0', + child: const TDRadio( + id: 'index:0', + title: '单选', + ), + ), + const SizedBox( + height: 17, + ), + TDRadioGroup( + radioCheckStyle: TDRadioStyle.hollowCircle, + selectId: 'index:0', + child: const TDRadio( + id: 'index:0', + title: '单选', + ), + ) + ], + ); + } + + @Demo(group: 'radio') + Widget _checkPosition(BuildContext context) { + return Column( + children: [ + TDRadioGroup( + contentDirection: TDContentDirection.right, + selectId: 'index:0', + child: const TDRadio( + id: 'index:0', + title: '单选', + ), + ), + TDRadioGroup( + contentDirection: TDContentDirection.left, + selectId: 'index:0', + child: const TDRadio( + id: 'index:0', + title: '单选', + showDivider: false, + ), + ) + ], + ); + } + + @Demo(group: 'radio') + Widget _passThroughStyle(BuildContext context) { + return TDRadioGroup( + selectId: 'index:0', + passThrough: true, + child: ListView.builder( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + var title = '单选'; + return TDRadio( + id: 'index:$index', + title: title, + size: TDCheckBoxSize.large, + ); + }, + itemCount: 4, + ), + ); + } + + @Demo(group: 'radio') + Widget _verticalCardStyle(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + cardMode: true, + direction: Axis.vertical, + directionalTdRadios: const [ + TDRadio( + id: 'index:0', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDRadio( + id: 'index:1', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDRadio( + id: 'index:2', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + TDRadio( + id: 'index:3', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息', + cardMode: true, + ), + ], + ); + } + + @Demo(group: 'radio') + Widget _horizontalCardStyle(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + cardMode: true, + direction: Axis.horizontal, + rowCount: 2, + directionalTdRadios: const [ + TDRadio( + id: 'index:0', + title: '单选', + cardMode: true, + ), + TDRadio( + id: 'index:1', + title: '单选', + cardMode: true, + ), + TDRadio( + id: 'index:2', + title: '单选', + cardMode: true, + ), + TDRadio( + id: 'index:3', + title: '单选', + cardMode: true, + ), + ], + ); + } + + @Demo(group: 'radio') + Widget _showBottomLine(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + direction: Axis.horizontal, + showDivider: true, + directionalTdRadios: const [ + TDRadio( + id: '0', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '1', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '2', + title: '上限四字', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + ], + ); + } + + @Demo(group: 'radio') + Widget _customBottomLine(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + direction: Axis.horizontal, + showDivider: true, + divider: const TDDivider( + height: 20, + color: Colors.red, + ), + directionalTdRadios: const [ + TDRadio( + id: '0', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '1', + title: '单选标题', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + TDRadio( + id: '2', + title: '上限四字', + radioStyle: TDRadioStyle.circle, + showDivider: false, + ), + ], + ); + } + + @Demo(group: 'radio') + Widget _customColorAndFont(BuildContext context) { + return TDRadioGroup( + selectId: 'index:1', + child: ListView( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + children: [ + TDRadio( + id: 'index:1', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + TDRadio( + id: 'index:2', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '单选标题多行单选标题多行单选标题多行单选标题多行单选标题多行单选标题多行', + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + TDRadio( + id: 'index:3', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息', + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + TDRadio( + id: 'index:4', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '单选标题多行单选标题多行单选标题多行单选标题多行单选标题多行单选标题多行', + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + radioStyle: TDRadioStyle.hollowCircle, + ), + TDRadio( + id: 'index:6', + title: '绿色', + titleColor: Colors.green, + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '我是蓝色并且有灰色背景', + subTitleColor: Colors.blue, + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + backgroundColor: TDTheme.of(context).grayColor2, + ), + TDRadio( + id: 'index:5', + title: '单选', + titleMaxLine: 2, + subTitleMaxLine: 2, + subTitle: '描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息', + selectColor: TDTheme.of(context).errorColor3, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + cardMode: true, + ), + ], + ), + ); + } + + @Demo(group: 'radio') + Widget _customDisableColorAndFont(BuildContext context) { + return TDRadioGroup( + contentDirection: TDContentDirection.right, + selectId: '0', + child: Column( + children: [ + TDRadio( + id: '0', + title: '选项禁用-已选', + subTitle: '描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息描述信息', + radioStyle: TDRadioStyle.circle, + enable: false, + disableColor: TDTheme.of(context).errorColor1, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + TDRadio( + id: '1', + title: '选项禁用-默认', + radioStyle: TDRadioStyle.circle, + enable: false, + disableColor: TDTheme.of(context).errorColor1, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ), + ], + ), + ); + } + + @Demo(group: '') + Widget _customRadioLeftSpace(BuildContext context) { + return TDRadio( + id: '0', + title: '选项禁用-已选', + subTitle: '描述信息', + radioStyle: TDRadioStyle.circle, + checkBoxLeftSpace: 0, + disableColor: TDTheme.of(context).errorColor1, + titleFont: TDTheme.of(context).fontBodySmall, + subTitleFont: TDTheme.of(context).fontBodyExtraSmall, + ); + } +} diff --git a/tdesign-component/example/lib/page/td_radius_page.dart b/tdesign-component/example/lib/page/td_radius_page.dart new file mode 100644 index 000000000..be9e76197 --- /dev/null +++ b/tdesign-component/example/lib/page/td_radius_page.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +/// 圆角示例页面 +class TDRadiusPage extends StatelessWidget { + const TDRadiusPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(context), + exampleCodeGroup: 'radius', + children: [ + ExampleModule(title: '数值型', children: [ + ExampleItem(desc: '3px 极小组件圆角', builder: _buildRadiusSmall), + ExampleItem(desc: '6px 组件圆角', builder: _buildRadiusDefault), + ExampleItem(desc: '9px 卡片圆角', builder: _buildRadiusLarge), + ExampleItem(desc: '12px 面板圆角', builder: _buildRadiusExtraLarge), + ]), + ExampleModule(title: '特殊', children: [ + ExampleItem(desc: '胶囊型', builder: _buildRadiusRound), + ExampleItem(desc: '圆型', builder: _buildRadiusCircle), + ]), + ]); + } + + @Demo(group: 'radius') + Widget _buildRadiusSmall(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusSmall)), + ); + } + + @Demo(group: 'radius') + Widget _buildRadiusDefault(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + ); + } + + @Demo(group: 'radius') + Widget _buildRadiusLarge(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + ); + } + + @Demo(group: 'radius') + Widget _buildRadiusExtraLarge(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusExtraLarge)), + ); + } + + @Demo(group: 'radius') + Widget _buildRadiusRound(BuildContext context) { + // 胶囊型,数值设置较大 + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusRound)), + ); + } + + @Demo(group: 'radius') + Widget _buildRadiusCircle(BuildContext context) { + // 圆形与胶囊型一致,如果长款一致即是圆形 + return Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).brandNormalColor, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusCircle)), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_rate_page.dart b/tdesign-component/example/lib/page/td_rate_page.dart new file mode 100644 index 000000000..5984754ab --- /dev/null +++ b/tdesign-component/example/lib/page/td_rate_page.dart @@ -0,0 +1,161 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +/// +/// TDRate演示 +/// +class TDRatePage extends StatefulWidget { + const TDRatePage({Key? key}) : super(key: key); + + @override + State createState() { + return TDRatePageState(); + } +} + +class TDRatePageState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '用于对某行为/事物进行打分。', + exampleCodeGroup: 'rate', + backgroundColor: TDTheme.of(context).grayColor2, + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '实心评分', builder: _buildFilledRate), + ExampleItem(desc: '自定义评分', builder: _buildCusRate), + ExampleItem(desc: '自定义评分数量', builder: _buildNumRate), + ExampleItem(desc: '带描述评分', builder: _buildMsgRate), + ExampleItem(desc: '评分弹框位置', builder: _buildDRate), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '只可选全星时', builder: _buildFullRate), + ExampleItem(desc: '可选半星时', builder: _buildHalfRate), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem(desc: '评分大小', builder: _buildSizeRate), + ExampleItem(desc: '设置评分颜色', builder: _buildColorRate), + ]), + ExampleModule(title: '特殊样式', children: [ + ExampleItem(desc: '竖向带描述评分', builder: _buildOtherRate), + ]), + ]); + } + + @Demo(group: 'rate') + Widget _buildFilledRate(BuildContext context) { + return const TDCell(title: '实心评分', noteWidget: TDRate(value: 3)); + } + + @Demo(group: 'rate') + Widget _buildCusRate(BuildContext context) { + return const TDCell(title: '自定义评分', noteWidget: TDRate(value: 3, icon: [TDIcons.thumb_up])); + } + + @Demo(group: 'rate') + Widget _buildNumRate(BuildContext context) { + return const TDCell( + title: '自定义评分数量', + noteWidget: TDRate( + value: 2, + count: 3, + )); + } + + @Demo(group: 'rate') + Widget _buildMsgRate(BuildContext context) { + return Column(children: const [ + TDCell(title: '带描述评分', noteWidget: TDRate(value: 3, showText: true, texts: ['1分', '2分', '3分', '4分', '5分'])), + SizedBox(height: 16), + TDCell(title: '带描述评分', noteWidget: TDRate(value: 3, showText: true)) + ]); + } + + @Demo(group: 'rate') + Widget _buildDRate(BuildContext context) { + return Column(children: const [ + TDCell(title: '顶部显示', noteWidget: TDRate(placement: PlacementEnum.top)), + SizedBox(height: 16), + TDCell(title: '不显示', noteWidget: TDRate(placement: PlacementEnum.none)), + SizedBox(height: 16), + TDCell(title: '底部显示', noteWidget: TDRate(placement: PlacementEnum.bottom)), + ]); + } + + @Demo(group: 'rate') + Widget _buildFullRate(BuildContext context) { + return const TDCell(title: '点击活滑动', noteWidget: TDRate(value: 3)); + } + + @Demo(group: 'rate') + Widget _buildHalfRate(BuildContext context) { + return const TDCell(title: '点击活滑动', noteWidget: TDRate(value: 3, allowHalf: true, onChange: print,)); + } + + @Demo(group: 'rate') + Widget _buildSizeRate(BuildContext context) { + return Column(children: const [ + TDCell(title: '默认尺寸24', noteWidget: TDRate(value: 3)), + SizedBox(height: 16), + TDCell(title: '小尺寸20', noteWidget: TDRate(value: 3, size: 20)), + ]); + } + + @Demo(group: 'rate') + Widget _buildColorRate(BuildContext context) { + return Column(children: const [ + TDCell( + title: '填充评分', + noteWidget: TDRate( + value: 2.5, + allowHalf: true, + color: [Color(0xFFFFC51C), Color(0xFFE8E8E8)], + )), + SizedBox(height: 16), + TDCell(title: '线描评分', noteWidget: TDRate(value: 2.5, allowHalf: true, color: [Color(0xFF00A870)])), + ]); + } + + @Demo(group: 'rate') + Widget _buildOtherRate(BuildContext context) { + var texts = ['非常糟糕', '有些糟糕', '可以尝试', '可以前往', '推荐前往']; + return Container( + width: double.infinity, + child: Center( + child: TDRate( + value: 2, + size: 30, + showText: true, + // texts: ['非常糟糕', '有些糟糕', '可以尝试', '可以前往', '推荐前往'], + direction: Axis.vertical, + // mainAxisAlignment: MainAxisAlignment.center, + // textWidth: 64, + builderText: (context, value) { + return value == 0 + ? const SizedBox.shrink() + : Padding( + padding: EdgeInsets.only(top: TDTheme.of(context).spacer8), + child: TDText( + texts[(value - 1).toInt()], + font: TDTheme.of(context).fontTitleMedium, + textColor: TDTheme.of(context).warningColor5, + ), + ); + }, + ), + ), + + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + color: Colors.white, + ); + } +} diff --git a/tdesign-component/example/lib/page/td_refresh_page.dart b/tdesign-component/example/lib/page/td_refresh_page.dart new file mode 100644 index 000000000..92c8211f1 --- /dev/null +++ b/tdesign-component/example/lib/page/td_refresh_page.dart @@ -0,0 +1,84 @@ +/* + * Created by haozhicao@tencent.com on 6/28/22. + * td_loading_page.dart + * + */ + +import 'package:easy_refresh/easy_refresh.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TdPullDownRefreshPage extends StatefulWidget { + const TdPullDownRefreshPage({Key? key}) : super(key: key); + + @override + State createState() => _TdPullDownRefreshPageState(); +} + +class _TdPullDownRefreshPageState extends State { + var count = 0; + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + exampleCodeGroup: 'refresh', + desc: '用于快速刷新页面信息,刷新可以是整页刷新也可以是页面的局部刷新。', + showSingleChild: true, + singleChild: CodeWrapper(builder: _buildRefresh), + ); + } + + @Demo(group: 'refresh') + Widget _buildRefresh(BuildContext context) { + return EasyRefresh( + // 下拉样式 + header: TDRefreshHeader(), + child: SingleChildScrollView( + child: Column( + children: [ + Container( + height: 171, + alignment: Alignment.center, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.all(Radius.circular(TDTheme.of(context).radiusLarge))), + margin: const EdgeInsets.only(left: 16, right: 16), + child: TDText( + PlatformUtil.isWeb ? 'Web暂不支持下拉,请下载安装apk体验' : '拖拽该区域演示 顶部下拉刷新', + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).fontGyColor4, + ), + ), + Container( + height: 70, + alignment: Alignment.center, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.all(Radius.circular(TDTheme.of(context).radiusLarge))), + margin: const EdgeInsets.only(top: 16, left: 16, right: 16), + child: TDText( + '下拉刷新次数:${count}', + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).fontGyColor4, + ), + ), + const SizedBox(height: 500), + ], + ), + ), + // 下拉刷新回调 + onRefresh: () { + Future.delayed(const Duration(seconds: 2), () { + setState(() { + count++; + }); + }); + }, + ); + } +} diff --git a/tdesign-component/example/lib/page/td_result_page.dart b/tdesign-component/example/lib/page/td_result_page.dart new file mode 100644 index 000000000..1f43052ad --- /dev/null +++ b/tdesign-component/example/lib/page/td_result_page.dart @@ -0,0 +1,206 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDResultPage extends StatefulWidget { + const TDResultPage({Key? key}) : super(key: key); + + @override + State createState() => _TDResultPageState(); +} + +class _TDResultPageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + title: 'Result 结果', + desc: '反馈结果状态。', + exampleCodeGroup: 'result', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + desc: '基础结果', ignoreCode: true, builder: _buildBasicResult), + ExampleItem( + desc: '带描述的结果', + ignoreCode: true, + builder: _buildResultWithDescription), + ExampleItem( + desc: '自定义结果', ignoreCode: true, builder: _buildCustomResult), + ExampleItem( + desc: '页面示例', ignoreCode: true, builder: _buildPageExample), + ]), + ], + ); + } + + Widget _buildBasicResult(BuildContext context) { + return Column( + children: [ + CodeWrapper( + builder: _buildBasicResultSuccess, + ), + const SizedBox(height: 48), + CodeWrapper( + builder: _buildBasicResultError, + ), + const SizedBox(height: 48), + CodeWrapper( + builder: _buildBasicResultWarning, + ), + const SizedBox(height: 48), + CodeWrapper( + builder: _buildBasicResultDefault, + ), + ], + ); + } + + Widget _buildResultWithDescription(BuildContext context) { + return Column( + children: [ + CodeWrapper( + builder: _buildResultWithDescriptionSuccess, + ), + const SizedBox(height: 48), + CodeWrapper( + builder: _buildResultWithDescriptionError, + ), + const SizedBox(height: 48), + CodeWrapper( + builder: _buildResultWithDescriptionWarning, + ), + const SizedBox(height: 48), + CodeWrapper( + builder: _buildResultWithDescriptionDefault, + ), + ], + ); + } + + Widget _buildCustomResult(BuildContext context) { + return CodeWrapper( + builder: _buildCustomResultContent, + ); + } + + Widget _buildPageExample(BuildContext context) { + return TDButton( + text: '页面示例', + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + type: TDButtonType.outline, + isBlock: true, + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => Scaffold( + appBar: AppBar( + title: const Text('Result 结果'), + ), + body: Column( + children: [ + const SizedBox(height: 48), + const TDResult( + title: '成功状态', + theme: TDResultTheme.success, + description: '描述文字', + ), + const SizedBox(height: 48), + TDButton( + text: '返回', + theme: TDButtonTheme.primary, + size: TDButtonSize.large, + type: TDButtonType.outline, + isBlock: true, + onTap: () { + Navigator.pop(context); + }, + ), + ], + ), + ), + ), + ); + }, + ); + } + + @Demo(group: 'result') + TDResult _buildBasicResultSuccess(BuildContext context) { + return const TDResult( + title: '成功状态', + theme: TDResultTheme.success, + ); + } + + @Demo(group: 'result') + TDResult _buildBasicResultError(BuildContext context) { + return const TDResult( + title: '失败状态', + theme: TDResultTheme.error, + ); + } + + @Demo(group: 'result') + TDResult _buildBasicResultWarning(BuildContext context) { + return const TDResult( + title: '警示状态', + theme: TDResultTheme.warning, + ); + } + + @Demo(group: 'result') + TDResult _buildBasicResultDefault(BuildContext context) { + return const TDResult( + title: '默认状态', + theme: TDResultTheme.defaultTheme, + ); + } + + @Demo(group: 'result') + TDResult _buildResultWithDescriptionSuccess(BuildContext context) { + return const TDResult( + title: '成功状态', + theme: TDResultTheme.success, + description: '描述文字', + ); + } + + @Demo(group: 'result') + TDResult _buildResultWithDescriptionError(BuildContext context) { + return const TDResult( + title: '失败状态', + theme: TDResultTheme.error, + description: '描述文字', + ); + } + + @Demo(group: 'result') + TDResult _buildResultWithDescriptionWarning(BuildContext context) { + return const TDResult( + title: '警示状态', + theme: TDResultTheme.warning, + description: '描述文字', + ); + } + + @Demo(group: 'result') + TDResult _buildResultWithDescriptionDefault(BuildContext context) { + return const TDResult( + title: '默认状态', + theme: TDResultTheme.defaultTheme, + description: '描述文字', + ); + } + + @Demo(group: 'result') + TDResult _buildCustomResultContent(BuildContext context) { + return TDResult( + title: '自定义结果', + icon: Image.asset('assets/img/illustration.png'), + description: '描述文字', + ); + } +} diff --git a/tdesign-component/example/lib/page/td_search_bar_page.dart b/tdesign-component/example/lib/page/td_search_bar_page.dart new file mode 100644 index 000000000..e5ca9c16d --- /dev/null +++ b/tdesign-component/example/lib/page/td_search_bar_page.dart @@ -0,0 +1,182 @@ +import 'package:flutter/cupertino.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +class TDSearchBarPage extends StatefulWidget { + const TDSearchBarPage({Key? key}) : super(key: key); + + @override + State createState() => _TDSearchBarPageState(); +} + +class _TDSearchBarPageState extends State { + String? inputText; + String? searchText; + TextEditingController inputController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '用于一组预设数据中的选择。', + exampleCodeGroup: 'search', + backgroundColor: TDTheme.of(context).grayColor2, + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '基础搜索框', builder: _buildDefaultSearchBar), + ExampleItem(desc: '获取焦点后显示取消按钮', builder: _buildFocusSearchBar), + ], + ), + ExampleModule(title: '组件样式', children: [ + ExampleItem(desc: '搜索框形状', builder: _buildSearchBarWithShape), + ExampleItem(desc: '默认状态其他对齐方式', builder: _buildCenterSearchBar), + ]), + ], + test: [ + ExampleItem(desc: '获取焦点后显示自定义操作按钮', builder: _buildSearchBarWithAction), + ExampleItem( + desc: '自定义获取焦点后显示按钮', builder: _buildFocusSearchBarWithAction), + ], + ); + } + + @Demo(group: 'search') + Widget _buildDefaultSearchBar(BuildContext context) { + return _buildColumnWidgets( + context, + TDSearchBar( + placeHolder: '搜索预设文案', + onTextChanged: (String text) { + setState(() { + inputText = text; + }); + }, + )); + } + + @Demo(group: 'search') + Widget _buildColumnWidgets(BuildContext context, Widget widget) { + return Column( + children: [ + widget, + const SizedBox( + height: 16, + ), + ], + ); + } + + @Demo(group: 'search') + Widget _buildFocusSearchBar(BuildContext context) { + return const TDSearchBar( + placeHolder: '搜索预设文案', + needCancel: true, + autoFocus: true, + ); + } + + @Demo(group: 'search') + Widget _buildSearchBarWithShape(BuildContext context) { + return Column( + children: [ + _buildColumnWidgets( + context, + TDSearchBar( + placeHolder: '搜索预设文案', + style: TDSearchStyle.square, + onTextChanged: (String text) { + setState(() { + inputText = text; + }); + }, + ), + ), + _buildColumnWidgets( + context, + TDSearchBar( + placeHolder: '搜索预设文案', + style: TDSearchStyle.round, + onTextChanged: (String text) { + setState(() { + inputText = text; + }); + }, + ), + ), + ], + ); + } + + @Demo(group: 'search') + Widget _buildCenterSearchBar(BuildContext context) { + return TDSearchBar( + placeHolder: '搜索预设文案', + alignment: TDSearchAlignment.center, + onTextChanged: (String text) { + setState(() { + inputText = text; + }); + }, + ); + } + + @Demo(group: 'search') + Widget _buildSearchBarWithAction(BuildContext context) { + return Column( + children: [ + TDSearchBar( + placeHolder: '搜索预设文案', + alignment: TDSearchAlignment.left, + action: '搜索', + onActionClick: (String text) { + setState(() { + searchText = text; + }); + }, + onTextChanged: (String text) { + setState(() { + inputText = text; + }); + }, + ), + const SizedBox( + height: 10, + ), + Container( + padding: const EdgeInsets.only(left: 15), + alignment: Alignment.centerLeft, + child: TDText( + '搜索框输入的内容:${searchText ?? ''}', + ), + ) + ], + ); + } + + @Demo(group: 'search') + Widget _buildFocusSearchBarWithAction(BuildContext context) { + return TDSearchBar( + placeHolder: '搜索预设文案', + action: '搜索', + needCancel: true, + controller: inputController, + onActionClick: (value) { + showGeneralDialog( + context: context, + pageBuilder: (BuildContext buildContext, Animation animation, + Animation secondaryAnimation) { + return TDConfirmDialog( + content: inputController.text.isNotEmpty + ? '搜索关键词:${inputController.text}' + : '搜索关键词为空', + ); + }, + ); + }, + ); + } +} diff --git a/tdesign-component/example/lib/page/td_shadows_page.dart b/tdesign-component/example/lib/page/td_shadows_page.dart new file mode 100644 index 000000000..9c31d50e5 --- /dev/null +++ b/tdesign-component/example/lib/page/td_shadows_page.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +/// 圆角示例页面 +class TDShadowsPage extends StatelessWidget { + const TDShadowsPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ExamplePage( + backgroundColor: TDTheme.of(context).whiteColor1, + title: tdTitle(context), + exampleCodeGroup: 'shadows', + children: [ + ExampleModule(title: '投影', children: [ + ExampleItem(desc: '基础投影', builder: _buildShadowsBase), + ExampleItem(desc: '中层投影', builder: _buildShadowsMiddle), + ExampleItem(desc: '上层投影', builder: _buildShadowsTop), + ]), + ]); + } + + @Demo(group: 'shadows') + Widget _buildShadowsBase(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + boxShadow: TDTheme.of(context).shadowsBase, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + ); + } + + @Demo(group: 'shadows') + Widget _buildShadowsMiddle(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + boxShadow: TDTheme.of(context).shadowsMiddle, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + ); + } + + @Demo(group: 'shadows') + Widget _buildShadowsTop(BuildContext context) { + return Container( + width: 100, + height: 50, + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + boxShadow: TDTheme.of(context).shadowsTop, + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault)), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_skeleton_page.dart b/tdesign-component/example/lib/page/td_skeleton_page.dart new file mode 100644 index 000000000..b97f49a32 --- /dev/null +++ b/tdesign-component/example/lib/page/td_skeleton_page.dart @@ -0,0 +1,226 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +class TDSkeletonPage extends StatefulWidget { + const TDSkeletonPage({Key? key}) : super(key: key); + + @override + State createState() => _TDSkeletonPageState(); +} + +class _TDSkeletonPageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '当网络较慢时,在页面真实数据加载之前,给用户展示出页面的大致结构。', + exampleCodeGroup: 'skeleton', + backgroundColor: TDTheme.of(context).whiteColor1, + children: [ + ExampleModule( + title: '类型', + children: [ + ExampleItem( + desc: '头像骨架屏', + builder: _wrapper(_buildAvatarSkeleton), + methodName: '_buildAvatarSkeleton', + ), + ExampleItem( + desc: '图片骨架屏', + builder: _wrapper(_buildImageSkeleton), + methodName: '_buildImageSkeleton', + ), + ExampleItem( + desc: '文本骨架屏', + builder: _wrapper(_buildTextSkeleton, isFlexible: true), + methodName: '_buildTextSkeleton', + ), + ExampleItem( + desc: '段落骨架屏', + builder: _wrapper(_buildParagraphSkeleton, isFlexible: true), + methodName: '_buildParagraphSkeleton', + ), + ExampleItem( + desc: '单元格骨架屏', + builder: _wrapper(_buildCellSkeleton), + methodName: '_buildCellSkeleton', + ), + ExampleItem( + desc: '宫格骨架屏', + builder: _wrapper(_buildGridSkeleton), + methodName: '_buildGridSkeleton', + ), + ExampleItem( + desc: '图文组合骨架屏', + builder: _wrapper(_buildCombineSkeleton), + methodName: '_buildCombineSkeleton', + ), + ], + ), + ExampleModule( + title: '组件动效', + children: [ + ExampleItem( + desc: '渐变加载效果', + builder: _wrapper(_buildGradientSkeleton, isFlexible: true), + methodName: '_buildGradientSkeleton', + ), + ExampleItem( + desc: '闪烁加载效果', + builder: _wrapper(_buildFlashedSkeleton, isFlexible: true), + methodName: '_buildFlashedSkeleton', + ), + ], + ), + ]); + } + + Widget Function(BuildContext) _wrapper( + Function(BuildContext) builder, { + bool isFlexible = false, + }) => + (context) => Container( + alignment: Alignment.topLeft, + padding: EdgeInsets.fromLTRB( + TDTheme.of(context).spacer16, + 0, + TDTheme.of(context).spacer16, + 0, + ), + child: isFlexible + ? Row(children: [builder(context)]) + : builder(context), + ); + + @Demo(group: 'skeleton') + Widget _buildAvatarSkeleton(BuildContext context) { + return TDSkeleton(theme: TDSkeletonTheme.avatar); + } + + @Demo(group: 'skeleton') + Widget _buildImageSkeleton(BuildContext context) { + return TDSkeleton(theme: TDSkeletonTheme.image); + } + + @Demo(group: 'skeleton') + Widget _buildTextSkeleton(BuildContext context) { + return TDSkeleton(theme: TDSkeletonTheme.text); + } + + @Demo(group: 'skeleton') + Widget _buildParagraphSkeleton(BuildContext context) { + return TDSkeleton(theme: TDSkeletonTheme.paragraph); + } + + @Demo(group: 'skeleton') + Widget _buildCellSkeleton(BuildContext context) { + var rowColsAvatar = TDSkeleton(theme: TDSkeletonTheme.avatar); + var rowColsImage = TDSkeleton.fromRowCol( + rowCol: TDSkeletonRowCol(objects: const [ + [TDSkeletonRowColObj.rect(width: 48, height: 48, flex: null)] + ]), + ); + var rowColsContent = TDSkeleton.fromRowCol( + rowCol: TDSkeletonRowCol( + objects: const [ + [TDSkeletonRowColObj(), TDSkeletonRowColObj.spacer(flex: 1)], + [TDSkeletonRowColObj()] + ], + ), + ); + + return Column( + children: [ + Row( + children: [ + rowColsAvatar, + const SizedBox(width: 12), + rowColsContent, + ], + ), + const SizedBox(height: 16), + Row( + children: [ + rowColsImage, + const SizedBox(width: 12), + rowColsContent, + ], + ), + ], + ); + } + + @Demo(group: 'skeleton') + Widget _buildGridSkeleton(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + for (var i = 0; i < 5; i++) + TDSkeleton.fromRowCol( + rowCol: TDSkeletonRowCol(objects: const [ + [TDSkeletonRowColObj.rect(width: 48, height: 48, flex: null)], + [TDSkeletonRowColObj.text(width: 48, flex: null)], + ]), + ), + ], + ); + } + + @Demo(group: 'skeleton') + Widget _buildCombineSkeleton(BuildContext context) { + var rowCols = Flexible( + child: LayoutBuilder( + builder: (context, constraints) => Row(children: [ + TDSkeleton.fromRowCol( + rowCol: TDSkeletonRowCol( + objects: [ + [ + TDSkeletonRowColObj( + width: constraints.maxWidth*0.96, + height: constraints.maxWidth, + flex: null, + style: TDSkeletonRowColObjStyle( + borderRadius: (context) => + TDTheme.of(context).radiusExtraLarge)) + ], + [TDSkeletonRowColObj.text( + width: constraints.maxWidth*0.96, + )], + const [ + TDSkeletonRowColObj.text(), + TDSkeletonRowColObj.spacer(flex: 1), + ], + ], + ), + ) + ]))); + + return Row( + children: [ + rowCols, + SizedBox(width: TDTheme.of(context).spacer4), + rowCols, + ], + ); + } + + @Demo(group: 'skeleton') + Widget _buildGradientSkeleton(BuildContext context) { + return TDSkeleton( + animation: TDSkeletonAnimation.gradient, + theme: TDSkeletonTheme.paragraph, + ); + } + + @Demo(group: 'skeleton') + Widget _buildFlashedSkeleton(BuildContext context) { + return TDSkeleton( + animation: TDSkeletonAnimation.flashed, + theme: TDSkeletonTheme.paragraph, + ); + } +} diff --git a/tdesign-component/example/lib/page/td_slider_page.dart b/tdesign-component/example/lib/page/td_slider_page.dart new file mode 100644 index 000000000..fa51ff4c4 --- /dev/null +++ b/tdesign-component/example/lib/page/td_slider_page.dart @@ -0,0 +1,671 @@ +/// +/// Created by arvinwli@tencent.com on 4/24/23. +/// +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDSliderPage extends StatefulWidget { + const TDSliderPage({Key? key}) : super(key: key); + + @override + State createState() => _TDSliderPageState(); +} + +class DisplayRangeData { + final Position currentPosition; + final double currentTapValue; + final Offset? tapOffset; + DisplayRangeData({ + required this.currentPosition, + required this.currentTapValue, + this.tapOffset, + }); +} + +class _TDSliderPageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '用于选择横轴上的数值、区间、档位。', + exampleCodeGroup: 'slider', + padding: const EdgeInsets.only(top: 8, bottom: 8), + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '单游标滑块', builder: _buildSingleHandle), + ExampleItem(desc: '双游标滑块', builder: _buildDoubleHandle), + ExampleItem( + desc: '带数值单游标滑块 ', builder: _buildSingleHandleWithNumber), + ExampleItem( + desc: '带数值双游标滑块', builder: _buildDoubleHandleWithNumber), + ExampleItem(desc: '带刻度单游标滑块', builder: _buildSingleHandleWithScale), + ExampleItem(desc: '带刻度双游标滑块', builder: _buildDoubleHandleWithScale), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '禁用状态', builder: _buildDisableSingleHandle), + ExampleItem(builder: _buildDisableDoubleHandleWithNumber), + ExampleItem(builder: _buildDisableDoubleHandleWithScale), + ]), + ExampleModule(title: '组件事件', children: [ + ExampleItem(desc: 'onTap', builder: _buildOnTapSingleHandle), + ExampleItem(builder: _buildOnTapDoubleHandle), + ExampleItem( + desc: 'onThumbTextTap', + builder: _buildOnThumbTextTapSingleHandle), + ExampleItem(builder: _buildOnThumbTextTapDoubleHandle), + ]), + ExampleModule(title: '特殊样式', children: [ + ExampleItem( + desc: '胶囊型滑块', builder: _buildCapsuleSingleHandleWithNumber), + ExampleItem(builder: _buildCapsuleDoubleHandle), + ExampleItem(builder: _buildCapsuleSingleHandle), + ExampleItem(builder: _buildCapsuleDoubleHandleWithNumber), + ExampleItem(builder: _buildCapsuleSingleHandleWithScale), + ExampleItem(builder: _buildCapsuleDoubleHandleWithScale), + ExampleItem(desc: '胶囊型滑块', builder: _buildCapsule), + ExampleItem(desc: '自定义盒子样式', builder: _buildCustomDecoration), + ExampleItem(desc: '自定义滑轨颜色', builder: _buildCustomActiveColor), + ]), + ]); + } + + @Demo(group: 'slider') + Widget _buildSingleHandle(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + ), + value: 10, + onChanged: (value) {}); + } + + @Demo(group: 'slider') + Widget _buildDoubleHandle(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + ), + value: const RangeValues(10, 60), + onChanged: (value) {}, + ); + } + + @Demo(group: 'slider') + Widget _buildSingleHandleWithNumber(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showThumbValue: true, + scaleFormatter: (value) => value.toInt().toString(), + min: 0, + max: 100, + ), + value: 10, + leftLabel: '0', + rightLabel: '100', + onChanged: (value) {}, + ); + } + + @Demo(group: 'slider') + Widget _buildDoubleHandleWithNumber(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showThumbValue: true, + min: 0, + max: 100, + scaleFormatter: (value) => value.round().toString(), + ), + leftLabel: '0', + rightLabel: '100', + value: const RangeValues(40, 60), + onChanged: (value) {}, + ); + } + + @Demo(group: 'slider') + Widget _buildSingleHandleWithScale(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: 60, + onChanged: (value) {}, + ); + } + + @Demo(group: 'slider') + Widget _buildDoubleHandleWithScale(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(40, 70), + onChanged: (value) {}, + ); + } + + @Demo(group: 'slider') + Widget _buildDisableSingleHandle(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + ), + leftLabel: '0', + rightLabel: '100', + value: 40, + ); + } + + @Demo(group: 'slider') + Widget _buildDisableDoubleHandleWithNumber(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showThumbValue: true, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + leftLabel: '0', + rightLabel: '100', + value: const RangeValues(20, 60), + ); + } + + @Demo(group: 'slider') + Widget _buildDisableDoubleHandleWithScale(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + ); + } + + @Demo(group: 'slider') + Widget _buildOnTapSingleHandle(BuildContext context) { + var currentValue = 40.0; + Offset? tapOffset; + + return StatefulBuilder( + builder: (context, setState) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Value: ${currentValue.toStringAsFixed(1)}'), + const SizedBox(width: 10), + if (tapOffset != null) + Text( + 'Tap at (${tapOffset!.dx.toStringAsFixed(0)}, ${tapOffset!.dy.toStringAsFixed(0)})'), + ], + ), + TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, min: 0, max: 100, showThumbValue: true), + leftLabel: '0', + rightLabel: '100', + value: currentValue, + onChanged: (value) {}, + onTap: (offset, value) { + setState(() { + currentValue = value; + tapOffset = offset; + }); + print('onTap offset: $offset, value: $value'); + }, + ), + ], + ); + }, + ); + } + + @Demo(group: 'slider') + Widget _buildOnTapDoubleHandle(BuildContext context) { + final displayRangeDataNotifier = ValueNotifier( + DisplayRangeData( + currentPosition: Position.start, + currentTapValue: 40.0, + tapOffset: null, + ), + ); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ValueListenableBuilder( + valueListenable: displayRangeDataNotifier, + builder: (context, data, child) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Position: ${data.currentPosition}'), + const SizedBox(width: 10), + Text('Value: ${data.currentTapValue.toStringAsFixed(1)}'), + const SizedBox(width: 10), + if (data.tapOffset != null) + Text( + 'Tap at (${data.tapOffset!.dx.toStringAsFixed(0)}, ${data.tapOffset!.dy.toStringAsFixed(0)})'), + ], + ); + }, + ), + const SizedBox(height: 10), + TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, min: 0, max: 100, showThumbValue: true), + leftLabel: '0', + rightLabel: '100', + value: const RangeValues(10, 60), + onChanged: (value) {}, + onTap: (position, offset, value) { + displayRangeDataNotifier.value = DisplayRangeData( + currentPosition: position, + currentTapValue: value, + tapOffset: offset, + ); + print('onTap offset: $offset, value: $value'); + }, + ), + ], + ); + } + + @Demo(group: 'slider') + Widget _buildOnThumbTextTapSingleHandle(BuildContext context) { + var currentValue = 40.0; + Offset? tapOffset; + + return StatefulBuilder( + builder: (context, setState) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Value: ${currentValue.toStringAsFixed(1)}'), + const SizedBox(width: 10), + if (tapOffset != null) + Text( + 'Tap at (${tapOffset!.dx.toStringAsFixed(0)}, ${tapOffset!.dy.toStringAsFixed(0)})'), + ], + ), + TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + showThumbValue: true, + ), + leftLabel: '0', + rightLabel: '100', + value: currentValue, + onChanged: (value) {}, + onThumbTextTap: (offset, value) { + setState(() { + currentValue = value; + tapOffset = offset; + }); + print('onTap offset: $offset, value: $value'); + }, + ), + ], + ); + }, + ); + } + + @Demo(group: 'slider') + Widget _buildOnThumbTextTapDoubleHandle(BuildContext context) { + final displayRangeDataNotifier = ValueNotifier( + DisplayRangeData( + currentPosition: Position.start, + currentTapValue: 40.0, + tapOffset: null, + ), + ); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ValueListenableBuilder( + valueListenable: displayRangeDataNotifier, + builder: (context, data, child) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Position: ${data.currentPosition}'), + const SizedBox(width: 10), + Text('Value: ${data.currentTapValue.toStringAsFixed(1)}'), + const SizedBox(width: 10), + if (data.tapOffset != null) + Text( + 'Tap at (${data.tapOffset!.dx.toStringAsFixed(0)}, ${data.tapOffset!.dy.toStringAsFixed(0)})'), + ], + ); + }, + ), + const SizedBox(height: 10), + TDRangeSlider( + sliderThemeData: TDSliderThemeData( + context: context, min: 0, max: 100, showThumbValue: true), + leftLabel: '0', + rightLabel: '100', + value: const RangeValues(10, 60), + onChanged: (value) {}, + onThumbTextTap: (position, offset, value) { + displayRangeDataNotifier.value = DisplayRangeData( + currentPosition: position, + currentTapValue: value, + tapOffset: offset, + ); + print('onTap offset: $offset, value: $value'); + }, + ), + ], + ); + } + + @Demo(group: 'slider') + Widget _buildCapsuleSingleHandleWithNumber(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showThumbValue: true, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: 40, + onChanged: (value) {}, + ); + } + + @Demo(group: 'slider') + Widget _buildCapsuleDoubleHandle(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + onChanged: (value) {}, + ); + } + + @Demo(group: 'slider') + Widget _buildCapsuleSingleHandle(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + leftLabel: '0', + rightLabel: '100', + value: 40, + onChanged: (value) {}, + ); + } + + @Demo(group: 'slider') + Widget _buildCapsuleDoubleHandleWithNumber(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showThumbValue: true, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + leftLabel: '0', + rightLabel: '100', + value: const RangeValues(20, 60), + onChanged: (value) {}, + ); + } + + @Demo(group: 'slider') + Widget _buildCapsuleSingleHandleWithScale(BuildContext context) { + return TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + )..updateSliderThemeData((data) => data.copyWith( + activeTickMarkColor: const Color(0xFFE7E7E7), + inactiveTickMarkColor: const Color(0xFFE7E7E7), + )), + value: 60, + onChanged: (value) {}, + ); + } + + @Demo(group: 'slider') + Widget _buildCapsule(BuildContext context) { + return Column( + children: [ + TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showThumbValue: true, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: 40, + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + leftLabel: '0', + rightLabel: '100', + value: 40, + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + showThumbValue: true, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + leftLabel: '0', + rightLabel: '100', + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + )..updateSliderThemeData((data) => data.copyWith( + activeTickMarkColor: const Color(0xFFE7E7E7), + inactiveTickMarkColor: const Color(0xFFE7E7E7), + )), + value: 60, + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + )..updateSliderThemeData((data) => data.copyWith( + activeTickMarkColor: const Color(0xFFE7E7E7), + inactiveTickMarkColor: const Color(0xFFE7E7E7), + )), + value: const RangeValues(20, 60), + // divisions: 5, + onChanged: (value) {}, + ) + ], + ); + } + + @Demo(group: 'slider') + Widget _buildCustomDecoration(BuildContext context) { + return Column( + children: [ + TDSlider( + sliderThemeData: TDSliderThemeData( + context: context, + min: 0, + max: 100, + ), + value: 40, + boxDecoration: const BoxDecoration(color: Colors.amber), + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + boxDecoration: const BoxDecoration(color: Colors.deepOrangeAccent), + value: const RangeValues(20, 60), + onChanged: (value) {}, + ), + ], + ); + } + + @Demo(group: 'slider') + Widget _buildCapsuleDoubleHandleWithScale(BuildContext context) { + return TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + context: context, + showScaleValue: true, + divisions: 5, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + )..updateSliderThemeData((data) => data.copyWith( + activeTickMarkColor: const Color(0xFFE7E7E7), + inactiveTickMarkColor: const Color(0xFFE7E7E7), + )), + value: const RangeValues(20, 60), + onChanged: (value) {}, + ); + } + + @Demo(group: 'slider') + Widget _buildCustomActiveColor(BuildContext context) { + return Column( + children: [ + TDSlider( + sliderThemeData: TDSliderThemeData( + activeTrackColor: Colors.red, + inactiveTrackColor: Colors.green, + context: context, + min: 0, + max: 100, + ), + value: 40, + // divisions: 5, + onChanged: (value) {}, + ), + const SizedBox( + height: 16, + ), + TDRangeSlider( + sliderThemeData: TDSliderThemeData.capsule( + activeTrackColor: Colors.green, + inactiveTrackColor: Colors.red, + context: context, + min: 0, + max: 100, + scaleFormatter: (value) => value.toInt().toString(), + ), + value: const RangeValues(20, 60), + onChanged: (value) {}, + ), + ], + ); + } +} diff --git a/tdesign-component/example/lib/page/td_stepper_page.dart b/tdesign-component/example/lib/page/td_stepper_page.dart new file mode 100644 index 000000000..9e4f79bc3 --- /dev/null +++ b/tdesign-component/example/lib/page/td_stepper_page.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; + +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDStepperPage extends StatefulWidget { + const TDStepperPage({Key? key}) : super(key: key); + + @override + State createState() => _TDStepperPageState(); +} + +class _TDStepperPageState extends State { + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + var currentFocus = FocusScope.of(context); + + if (!currentFocus.hasPrimaryFocus && + currentFocus.focusedChild != null) { + FocusManager.instance.primaryFocus!.unfocus(); + } + }, + child: ExamplePage( + title: tdTitle(), + desc: '用于数量的增减。', + exampleCodeGroup: 'stepper', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '基础步进器', builder: _buildStepperWithBase), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem( + desc: '最大最小状态', builder: _buildStepperWithMaxMinStatus), + ExampleItem(desc: '禁用状态', builder: _buildStepperWithDisableStatus) + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem(desc: '步进器样式', builder: _buildStepperWithTheme), + ExampleItem(desc: '步进器尺寸', builder: _buildStepperWithSize) + ]), + ]), + ); + } + + @Demo(group: 'stepper') + Widget _buildStepperWithBase(BuildContext context) { + return _buildRow(context, [ + const TDStepper( + theme: TDStepperTheme.filled, + ) + ]); + } + + @Demo(group: 'stepper') + Widget _buildStepperWithMaxMinStatus(BuildContext context) { + return _buildRow(context, [ + const TDStepper(theme: TDStepperTheme.filled, value: 0, min: 0), + const TDStepper(theme: TDStepperTheme.filled, value: 999, max: 999), + ]); + } + + @Demo(group: 'stepper') + Widget _buildStepperWithDisableStatus(BuildContext context) { + return _buildRow(context, [ + const TDStepper( + theme: TDStepperTheme.filled, + disabled: true, + ), + ]); + } + + @Demo(group: 'stepper') + Widget _buildStepperWithTheme(BuildContext context) { + return _buildRow(context, [ + const TDStepper(theme: TDStepperTheme.filled, value: 3), + const TDStepper(theme: TDStepperTheme.outline, value: 3), + const TDStepper(theme: TDStepperTheme.normal, value: 3), + ]); + } + + @Demo(group: 'stepper') + Widget _buildStepperWithSize(BuildContext context) { + return _buildRow(context, [ + const TDStepper( + size: TDStepperSize.large, theme: TDStepperTheme.filled, value: 3), + const TDStepper( + size: TDStepperSize.medium, theme: TDStepperTheme.filled, value: 3), + const TDStepper( + size: TDStepperSize.small, theme: TDStepperTheme.filled, value: 3), + ]); + } + + @Demo(group: 'stepper') + Widget _buildRow(BuildContext context, List stepperItems) { + final theme = TDTheme.of(context); + + return Container( + decoration: BoxDecoration( + color: theme.whiteColor1, + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: stepperItems + .map((item) => SizedBox( + width: (MediaQuery.of(context).size.width - 32) / 3, + child: item, + )) + .toList(), + ), + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_steps_page.dart b/tdesign-component/example/lib/page/td_steps_page.dart new file mode 100644 index 000000000..046367795 --- /dev/null +++ b/tdesign-component/example/lib/page/td_steps_page.dart @@ -0,0 +1,795 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDStepsPage extends StatefulWidget { + const TDStepsPage({Key? key}) : super(key: key); + + @override + State createState() => _TDStepsPageState(); +} + +class _TDStepsPageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + backgroundColor: TDTheme.of(context).whiteColor1, + title: tdTitle(), + exampleCodeGroup: 'steps', + desc: 'Steps步骤条', + children: [ + ExampleModule(title: '水平默认步骤条', children: [ + ExampleItem(desc: '水平默认步骤条1', builder: _buildBasicHSteps1), + ExampleItem(desc: '水平默认步骤条2', builder: _buildBasicHSteps2), + ExampleItem(desc: '水平默认步骤条3', builder: _buildBasicHSteps3), + ]), + ExampleModule(title: '水平图标步骤条', children: [ + ExampleItem(desc: '水平图标步骤条1', builder: _buildHIconSteps1), + ExampleItem(desc: '水平图标步骤条2', builder: _buildHIconSteps2), + ExampleItem(desc: '水平图标步骤条3', builder: _buildHIconSteps3), + ]), + ExampleModule(title: '水平简略步骤条', children: [ + ExampleItem(desc: '水平简略步骤条1', builder: _buildSimpleHSteps1), + ExampleItem(desc: '水平简略步骤条2', builder: _buildSimpleHSteps2), + ExampleItem(desc: '水平简略步骤条3', builder: _buildSimpleHSteps3), + ]), + ExampleModule(title: '水平错误状态步骤条', children: [ + ExampleItem(desc: '水平错误状态基本步骤条', builder: _buildHErrorSteps1), + ExampleItem(desc: '水平错误状态图标步骤条', builder: _buildHErrorSteps2), + ExampleItem(desc: '水平错误状态简略步骤条', builder: _buildHErrorSteps3), + ]), + ExampleModule(title: '垂直步骤条', children: [ + ExampleItem(desc: '垂直默认步骤条', builder: _buildVBasicSteps), + ExampleItem(desc: '垂直图标步骤条', builder: _buildVIconSteps), + ExampleItem(desc: '垂直简略步骤条', builder: _buildVSimpleSteps), + ExampleItem(desc: '垂直错误状态基本步骤条', builder: _buildVErrorBasicSteps), + ExampleItem(desc: '垂直错误状态图标步骤条', builder: _buildVErrorIconSteps), + ExampleItem(desc: '垂直错误状态简略步骤条', builder: _buildVErrorSimpleSteps), + ExampleItem( + desc: '垂直自定义标题基本步骤条', builder: _buildVCustomTitleBaseSteps), + ExampleItem( + desc: '垂直自定义内容基本步骤条', builder: _buildVCustomContentBaseSteps), + ]), + ExampleModule(title: 'Extension步骤条', children: [ + ExampleItem( + desc: 'Read-only Steps 纯展示水平步骤条', builder: _buildHReadOnlySteps), + ExampleItem( + desc: 'Read-only Steps 纯展示垂直步骤条', builder: _buildVReadOnlySteps), + ExampleItem( + desc: 'Vertical Customize Steps 垂直自定义步骤条', + builder: _buildVCustomizeSteps), + ]), + ], + ); + } + + List basicHStepsListData1 = [ + TDStepsItemData(title: 'Steps1', content: 'Content1'), + TDStepsItemData(title: 'Steps2', content: 'Content2'), + ]; + + /// 基本步骤1 + @Demo(group: 'steps') + Widget _buildBasicHSteps1(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: basicHStepsListData1, + ), + ) + ], + ), + ); + } + + List basicHStepsListData2 = [ + TDStepsItemData(title: 'Steps1', content: 'Content1'), + TDStepsItemData(title: 'Steps2', content: 'Content2'), + TDStepsItemData(title: 'Steps3', content: 'Content3'), + ]; + + /// 基本步骤2 + @Demo(group: 'steps') + Widget _buildBasicHSteps2(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: basicHStepsListData2, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + ), + ) + ], + ), + ); + } + + List basicHStepsListData3 = [ + TDStepsItemData(title: 'Steps1', content: 'Content1'), + TDStepsItemData(title: 'Steps2', content: 'Content2'), + TDStepsItemData(title: 'Steps3', content: 'Content3'), + TDStepsItemData(title: 'Steps4', content: 'Content4'), + ]; + List basicStepsListData4 = [ + TDStepsItemData( + title: 'Steps1', content: 'Content1', successIcon: TDIcons.call), + TDStepsItemData(title: 'Steps2', content: 'Content2'), + TDStepsItemData( + title: 'Steps3', content: 'Content3', successIcon: TDIcons.call), + TDStepsItemData( + title: 'Steps4', content: 'Content4', successIcon: TDIcons.call), + ]; + + /// 基本步骤3 + @Demo(group: 'steps') + Widget _buildBasicHSteps3(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: basicHStepsListData3, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + ), + ) + ], + ), + ); + } + + List hIconStepsListData1 = [ + TDStepsItemData( + title: 'Steps1', content: 'Content1', successIcon: TDIcons.call), + TDStepsItemData( + title: 'Steps2', content: 'Content2', successIcon: TDIcons.call), + ]; + + /// 水平图标步骤条1 + @Demo(group: 'steps') + Widget _buildHIconSteps1(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hIconStepsListData1, + direction: TDStepsDirection.horizontal, + activeIndex: 0, + ), + ) + ], + ), + ); + } + + List hIconStepsListData2 = [ + TDStepsItemData( + title: 'Steps1', content: 'Content1', successIcon: TDIcons.call), + TDStepsItemData( + title: 'Steps2', content: 'Content2', successIcon: TDIcons.call), + TDStepsItemData( + title: 'Steps3', content: 'Content3', successIcon: TDIcons.call), + ]; + + /// 水平图标步骤条1 + @Demo(group: 'steps') + Widget _buildHIconSteps2(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hIconStepsListData2, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + ), + ) + ], + ), + ); + } + + List hIconStepsListData3 = [ + TDStepsItemData( + title: 'Steps1', content: 'Content1', successIcon: TDIcons.call), + TDStepsItemData( + title: 'Steps2', content: 'Content2', successIcon: TDIcons.call), + TDStepsItemData( + title: 'Steps3', content: 'Content3', successIcon: TDIcons.call), + TDStepsItemData( + title: 'Steps4', content: 'Content4', successIcon: TDIcons.call), + ]; + + /// 水平图标步骤条1 + @Demo(group: 'steps') + Widget _buildHIconSteps3(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hIconStepsListData3, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + ), + ) + ], + ), + ); + } + + List simpleHStepsListData1 = [ + TDStepsItemData(title: 'Steps1', content: 'Content1'), + TDStepsItemData(title: 'Steps2', content: 'Content2'), + ]; + + /// 水平简略步骤条1 + @Demo(group: 'steps') + Widget _buildSimpleHSteps1(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: simpleHStepsListData1, + direction: TDStepsDirection.horizontal, + activeIndex: 0, + simple: true, + ), + ) + ], + ), + ); + } + + List simpleHStepsListData2 = [ + TDStepsItemData(title: 'Steps1', content: 'Content1'), + TDStepsItemData(title: 'Steps2', content: 'Content2'), + TDStepsItemData(title: 'Steps3', content: 'Content3'), + ]; + + /// 水平简略步骤条2 + @Demo(group: 'steps') + Widget _buildSimpleHSteps2(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: simpleHStepsListData2, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + simple: true, + ), + ) + ], + ), + ); + } + + List simpleHStepsListData3 = [ + TDStepsItemData(title: 'Steps1', content: 'Content1'), + TDStepsItemData(title: 'Steps2', content: 'Content2'), + TDStepsItemData(title: 'Steps3', content: 'Content3'), + TDStepsItemData(title: 'Steps4', content: 'Content4'), + ]; + + /// 水平简略步骤条3 + @Demo(group: 'steps') + Widget _buildSimpleHSteps3(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: simpleHStepsListData3, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + simple: true, + ), + ) + ], + ), + ); + } + + List hErrorStepsListData1 = [ + TDStepsItemData(title: 'Steps1', content: 'Content1'), + TDStepsItemData(title: 'Error', content: 'Content2'), + TDStepsItemData(title: 'Steps3', content: 'Content3'), + TDStepsItemData(title: 'Steps4', content: 'Content4'), + ]; + + /// 水平简略步骤条3 + @Demo(group: 'steps') + Widget _buildHErrorSteps1(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hErrorStepsListData1, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + status: TDStepsStatus.error, + ), + ) + ], + ), + ); + } + + List hErrorStepsListData2 = [ + TDStepsItemData( + title: 'Steps1', content: 'Content1', successIcon: TDIcons.call), + TDStepsItemData( + title: 'Error', + content: 'Content2', + successIcon: TDIcons.call, + errorIcon: TDIcons.close_circle), + TDStepsItemData( + title: 'Steps3', content: 'Content3', successIcon: TDIcons.call), + TDStepsItemData( + title: 'Steps4', content: 'Content4', successIcon: TDIcons.call), + ]; + + /// 水平简略步骤条3 + @Demo(group: 'steps') + Widget _buildHErrorSteps2(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hErrorStepsListData2, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + status: TDStepsStatus.error, + ), + ) + ], + ), + ); + } + + List hErrorStepsListData3 = [ + TDStepsItemData( + title: 'Steps1', content: 'Content1', successIcon: TDIcons.call), + TDStepsItemData( + title: 'Error', + content: 'Content2', + successIcon: TDIcons.call, + errorIcon: TDIcons.close_circle), + TDStepsItemData( + title: 'Steps3', content: 'Content3', successIcon: TDIcons.call), + TDStepsItemData( + title: 'Steps4', content: 'Content4', successIcon: TDIcons.call), + ]; + + /// 水平简略步骤条3 + @Demo(group: 'steps') + Widget _buildHErrorSteps3(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hErrorStepsListData3, + direction: TDStepsDirection.horizontal, + activeIndex: 1, + status: TDStepsStatus.error, + simple: true, + ), + ) + ], + ), + ); + } + + List vBasicStepsListData = [ + TDStepsItemData(title: 'Filish', content: 'Customize content'), + TDStepsItemData(title: 'Process', content: 'Customize content'), + TDStepsItemData(title: 'Default', content: 'Customize content'), + TDStepsItemData(title: 'Default', content: 'Customize content'), + ]; + + /// 垂直默认步骤条 + @Demo(group: 'steps') + Widget _buildVBasicSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vBasicStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + ), + ) + ], + ), + ); + } + + List vIconStepsListData = [ + TDStepsItemData( + title: 'Filish', + content: 'Customize content', + successIcon: TDIcons.cart), + TDStepsItemData( + title: 'Process', + content: 'Customize content', + successIcon: TDIcons.cart), + TDStepsItemData( + title: 'Default', + content: 'Customize content', + successIcon: TDIcons.cart), + TDStepsItemData( + title: 'Default', + content: 'Customize content', + successIcon: TDIcons.cart), + ]; + + /// 垂直图标步骤条 + @Demo(group: 'steps') + Widget _buildVIconSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vIconStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + ), + ) + ], + ), + ); + } + + List vSimpleStepsListData = [ + TDStepsItemData( + title: 'Filish', + content: 'Customize content', + successIcon: TDIcons.cart), + TDStepsItemData( + title: 'Process', + content: 'Customize content', + successIcon: TDIcons.cart), + TDStepsItemData( + title: 'Default', + content: 'Customize content', + successIcon: TDIcons.cart), + TDStepsItemData( + title: 'Default', + content: 'Customize content', + successIcon: TDIcons.cart), + ]; + + /// 垂直简略步骤条 + @Demo(group: 'steps') + Widget _buildVSimpleSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vSimpleStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + simple: true, + ), + ) + ], + ), + ); + } + + List vErrorBasicStepsListData = [ + TDStepsItemData(title: 'Filish', content: 'Customize content'), + TDStepsItemData(title: 'Process', content: 'Customize content'), + TDStepsItemData(title: 'Default', content: 'Customize content'), + TDStepsItemData(title: 'Default', content: 'Customize content'), + ]; + + /// 垂直错误状态基本步骤条 + @Demo(group: 'steps') + Widget _buildVErrorBasicSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vErrorBasicStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + status: TDStepsStatus.error, + ), + ) + ], + ), + ); + } + + List vErrorIconStepsListData = [ + TDStepsItemData( + title: 'Filish', + content: 'Customize content', + successIcon: TDIcons.cart), + TDStepsItemData( + title: 'Process', + content: 'Customize content', + successIcon: TDIcons.cart, + errorIcon: TDIcons.close_circle), + TDStepsItemData( + title: 'Default', + content: 'Customize content', + successIcon: TDIcons.cart), + TDStepsItemData( + title: 'Default', + content: 'Customize content', + successIcon: TDIcons.cart), + ]; + + /// 垂直错误状态图标步骤条 + @Demo(group: 'steps') + Widget _buildVErrorIconSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vErrorIconStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + status: TDStepsStatus.error, + ), + ) + ], + ), + ); + } + + List vErrorSimpleStepsListData = [ + TDStepsItemData( + title: 'Filish', + content: 'Customize content', + successIcon: TDIcons.cart), + TDStepsItemData( + title: 'Process', + content: 'Customize content', + successIcon: TDIcons.cart), + TDStepsItemData( + title: 'Default', + content: 'Customize content', + successIcon: TDIcons.cart), + TDStepsItemData( + title: 'Default', + content: 'Customize content', + successIcon: TDIcons.cart), + ]; + + /// 垂直错误状态图标步骤条 + @Demo(group: 'steps') + Widget _buildVErrorSimpleSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vErrorSimpleStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + simple: true, + status: TDStepsStatus.error, + ), + ) + ], + ), + ); + } + + List vCustomTitleBasicStepsListData = [ + TDStepsItemData(title: 'Filish', content: 'Customize content'), + TDStepsItemData( + title: 'Process', + content: 'Customize content', + customTitle: Container( + margin: const EdgeInsets.only(bottom: 16, top: 4), + child: const Text( + '这是一个很长很长的自定义标题,可以自动换行的一个标题内容', + softWrap: true, + maxLines: 2, + overflow: TextOverflow.visible, + ), + )), + TDStepsItemData(title: 'Default', content: 'Customize content'), + TDStepsItemData(title: 'Default', content: 'Customize content'), + ]; + + List vCustomContentBasicStepsListData = [ + TDStepsItemData(title: 'Filish', content: 'Customize content'), + TDStepsItemData( + title: '这是一个很长很长很长很长的文字,他是用来展示这个步骤的标题', + content: 'Customize content', + customContent: Container( + margin: const EdgeInsets.only(bottom: 16, top: 4), + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: const TDImage( + assetUrl: 'assets/img/image.png', + type: TDImageType.square, + ), + ), + )), + TDStepsItemData(title: 'Default', content: 'Customize content'), + TDStepsItemData(title: 'Default', content: 'Customize content'), + ]; + + /// 垂直自定义标题基本步骤条 + @Demo(group: 'steps') + Widget _buildVCustomTitleBaseSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vCustomTitleBasicStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + ), + ) + ], + ), + ); + } + + /// 垂直自定义内容基本步骤条 + @Demo(group: 'steps') + Widget _buildVCustomContentBaseSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vCustomContentBasicStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 1, + ), + ) + ], + ), + ); + } + + List hReadOnlyStepsListData = [ + TDStepsItemData(title: 'Filish', content: 'content'), + TDStepsItemData(title: 'Process', content: 'content'), + TDStepsItemData(title: 'Default', content: 'content'), + TDStepsItemData(title: 'Default', content: 'content'), + ]; + + /// 水平自定义内容基本步骤条 + @Demo(group: 'steps') + Widget _buildHReadOnlySteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: hReadOnlyStepsListData, + readOnly: true, + ), + ) + ], + ), + ); + } + + List vReadOnlyStepsListData = [ + TDStepsItemData(title: 'Filish', content: 'Customize content'), + TDStepsItemData(title: 'Process', content: 'Customize content'), + TDStepsItemData(title: 'Default', content: 'Customize content'), + TDStepsItemData(title: 'Default', content: 'Customize content'), + ]; + + /// 垂直自定义内容基本步骤条 + @Demo(group: 'steps') + Widget _buildVReadOnlySteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vReadOnlyStepsListData, + direction: TDStepsDirection.vertical, + activeIndex: 0, + readOnly: true, + ), + ) + ], + ), + ); + } + + List vCustomizeStepsListData = [ + TDStepsItemData(title: 'Selected', content: ''), + TDStepsItemData(title: 'Selected', content: ''), + TDStepsItemData(title: 'Selected', content: ''), + TDStepsItemData(title: 'Please Selected', content: ''), + ]; + + /// Vertical Customize Steps 垂直自定义步骤条 + @Demo(group: 'steps') + Widget _buildVCustomizeSteps(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Row( + children: [ + Expanded( + flex: 1, + child: TDSteps( + steps: vCustomizeStepsListData, + direction: TDStepsDirection.vertical, + simple: true, + activeIndex: 3, + verticalSelect: true, + ), + ) + ], + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_swipe_cell_page.dart b/tdesign-component/example/lib/page/td_swipe_cell_page.dart new file mode 100644 index 000000000..d1811ad3e --- /dev/null +++ b/tdesign-component/example/lib/page/td_swipe_cell_page.dart @@ -0,0 +1,353 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDSwipeCellPage extends StatelessWidget { + const TDSwipeCellPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(context), + exampleCodeGroup: 'swipecell', + desc: '用于承载列表中的更多操作,通过左右滑动来展示,按钮的宽度固定高度根据列表高度而变化。', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem( + desc: '左滑单操作', + builder: _buildSwiperCell, + ), + ExampleItem( + desc: '左滑双操作', + builder: _buildSwiperMuliCell, + ), + ExampleItem( + desc: '左滑三操作', + builder: _buildSwiper3Cell, + ), + ExampleItem( + desc: '右滑单操作', + builder: _buildSwiperRightCell, + ), + ExampleItem( + desc: '左右滑操作', + builder: _buildSwiperRightLeftCell, + ), + ExampleItem( + desc: '带图标的滑动操作', + builder: _buildSwiperIconCell, + ), + ExampleItem( + desc: '带二次确认的操作', + builder: _buildSwiperConfirmCell, + ), + ], + ), + ], + test: const [], + ); + } + + @Demo(group: 'swipecell') + Widget _buildSwiperCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + var list = [ + {'id': '1', 'title': '左滑操作', 'note': '辅助信息', 'description': ''}, + {'id': '2', 'title': '左滑操作', 'note': '辅助信息', 'description': '一段很长很长的内容文字'}, + ]; + final cellLength = ValueNotifier(list.length); + return ValueListenableBuilder( + valueListenable: cellLength, + builder: (BuildContext context, value, Widget? child) { + return TDCellGroup( + cells: list.map((e) => TDCell(title: e['title'], note: e['note'], description: e['description'])).toList(), + builder: (context, cell, index) { + return TDSwipeCell( + slidableKey: ValueKey(list[index]['id']), + groupTag: 'test', + onChange: (direction, open) { + print('打开方向:$direction'); + print('打开转态$open'); + }, + right: TDSwipeCellPanel( + extentRatio: 60 / screenWidth, + // dragDismissible: true, + onDismissed: (context) { + list.removeAt(index); + cellLength.value = list.length; + }, + children: [ + TDSwipeCellAction( + backgroundColor: TDTheme.of(context).errorColor6, + label: '删除', + onPressed: (context) { + print('点击action'); + print(TDSwipeCell.of(context)); + print(TDSwipeCellInherited.of(context)?.controller); + list.removeAt(index); + cellLength.value = list.length; + }, + ), + ], + ), + cell: cell, + ); + }, + ); + }, + ); + } + + @Demo(group: 'swipecell') + Widget _buildSwiperMuliCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: 120 / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + label: '编辑', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + label: '删除', + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + ), + ); + } + + @Demo(group: 'swipecell') + Widget _buildSwiper3Cell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: 180 / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).brandColor7, + label: '保存', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + label: '编辑', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + label: '删除', + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + ), + ); + } + + @Demo(group: 'swipecell') + Widget _buildSwiperRightCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return TDSwipeCell( + groupTag: 'test', + left: TDSwipeCellPanel( + extentRatio: 60 / screenWidth, + children: [ + TDSwipeCellAction( + backgroundColor: TDTheme.of(context).brandColor7, + label: '选择', + ), + ], + ), + cell: const TDCell( + title: '右滑操作', + note: '辅助信息', + ), + ); + } + + @Demo(group: 'swipecell') + Widget _buildSwiperRightLeftCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return TDSwipeCell( + groupTag: 'test', + left: TDSwipeCellPanel( + extentRatio: 60 / screenWidth, + children: [ + TDSwipeCellAction( + backgroundColor: TDTheme.of(context).brandColor7, + label: '选择', + ), + ], + ), + right: TDSwipeCellPanel( + extentRatio: 120 / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + label: '编辑', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + label: '删除', + ), + ], + ), + cell: const TDCell( + title: '左右滑操作', + note: '辅助信息', + ), + ); + } + + @Demo(group: 'swipecell') + Widget _buildSwiperIconCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: 3, + itemBuilder: (context, index) { + if (index == 0) { + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: (80 + 80) / screenWidth, + children: [ + TDSwipeCellAction( + flex: 80, + backgroundColor: TDTheme.of(context).warningColor4, + icon: TDIcons.edit, + label: '编辑', + ), + TDSwipeCellAction( + flex: 80, + backgroundColor: TDTheme.of(context).errorColor6, + icon: TDIcons.delete, + label: '删除', + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + ), + ); + } else if (index == 1) { + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: 120 / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + icon: TDIcons.edit, + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + icon: TDIcons.delete, + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + ), + ); + } else { + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: 120 / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + direction: Axis.vertical, + icon: TDIcons.edit, + label: '编辑', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + direction: Axis.vertical, + icon: TDIcons.delete, + label: '删除', + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + description: '一段很长很长的内容文字', + ), + ); + } + }, + separatorBuilder: (context, index) { + return const SizedBox(height: 24); + }, + ); + } + + @Demo(group: 'swipecell') + Widget _buildSwiperConfirmCell(BuildContext context) { + // 屏幕宽度 + var screenWidth = MediaQuery.of(context).size.width; + return TDSwipeCell( + groupTag: 'test', + right: TDSwipeCellPanel( + extentRatio: (60 + 60) / screenWidth, + children: [ + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).warningColor4, + label: '编辑', + ), + TDSwipeCellAction( + flex: 60, + backgroundColor: TDTheme.of(context).errorColor6, + label: '删除', + ), + ], + confirms: [ + TDSwipeCellAction( + backgroundColor: TDTheme.of(context).errorColor6, + label: '确认删除', + confirmIndex: const [1], + ), + ], + ), + cell: const TDCell( + title: '左滑操作', + note: '辅助信息', + ), + ); + } +} diff --git a/tdesign-component/example/lib/page/td_swiper_page.dart b/tdesign-component/example/lib/page/td_swiper_page.dart new file mode 100644 index 000000000..a9a2a3b14 --- /dev/null +++ b/tdesign-component/example/lib/page/td_swiper_page.dart @@ -0,0 +1,404 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDSwiperPage extends StatelessWidget { + const TDSwiperPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(context), + exampleCodeGroup: 'swiper', + children: [ + ExampleModule(title: '组件类型', + children: [ + ExampleItem( + desc: '点状(dots)', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + margin: const EdgeInsets.only(left: 16,right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildDotsSwiper, + ), + ); + }), + ExampleItem( + desc: '点条状(dots-bar)', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + margin: const EdgeInsets.only(left: 16,right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildDotsBarSwiper, + ), + ); + }), + ExampleItem( + desc: '分式(fraction)', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + margin: const EdgeInsets.only(left: 16,right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildFractionSwiper, + ), + ); + }), + ExampleItem( + desc: '切换按钮(controls)', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + margin: const EdgeInsets.only(left: 16,right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildControlsSwiper, + ), + ); + }), + ExampleItem( + desc: '卡片式(cards)', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildCardsSwiper, + ), + ); + }), + ExampleItem( + desc: '卡片式(cards)-scale:0.8', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildScaleCardsSwiper, + ), + ); + }), + ], + ), + ExampleModule(title: '组件样式', children: [ + ExampleItem( + desc: '内部', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + margin: const EdgeInsets.only(left: 16,right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildDotsSwiper, + ), + ); + }), + ExampleItem( + desc: '外部', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + margin: const EdgeInsets.only(left: 16,right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildOuterDotsSwiper, + ), + ); + }), + ExampleItem( + desc: '右边(竖向)', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + margin: const EdgeInsets.only(left: 16,right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildRightDotsSwiper, + ), + ); + }), + ]) + ], + + test: [ + + ExampleItem( + desc: '卡片式(cards),只有两张不轮播', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildNotLoopCardsSwiper, + ), + ); + }), + ExampleItem( + // outer样式不支持竖向布局 + desc: '点条状outer样式', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + margin: const EdgeInsets.only(left: 16,right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildOuterDotsBarSwiper, + ), + ); + }), + ExampleItem( + desc: '分式符位置', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + margin: const EdgeInsets.only(left: 16,right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildFractionBarSwiper, + ), + ); + }), + ExampleItem( + desc: '竖向点条状', + ignoreCode: true, + builder: (_) { + return Container( + height: 193, + margin: const EdgeInsets.only(left: 16,right: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge)), + child: CodeWrapper( + builder: _buildVerticalDotsBarSwiper, + ), + ); + }), + ], + ); + } + + @Demo(group: 'swiper') + Widget _buildDotsSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } + + @Demo(group: 'swiper') + Widget _buildDotsBarSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + builder: TDSwiperPagination.dotsBar), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } + + @Demo(group: 'swiper') + Widget _buildFractionSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + builder: TDSwiperPagination.fraction), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } + + @Demo(group: 'swiper') + Widget _buildControlsSwiper(BuildContext context) { + return Swiper( + // autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.center, + builder: TDSwiperPagination.controls), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } + + @Demo(group: 'swiper') + Widget _buildCardsSwiper(BuildContext context) { + return Swiper( + viewportFraction: 0.75, + outer: true, + autoplay: true, + itemCount: 6, + loop: true, + transformer: TDPageTransformer.margin(), + pagination: const SwiperPagination( + alignment: Alignment.center, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } + + @Demo(group: 'swiper') + Widget _buildScaleCardsSwiper(BuildContext context) { + return Swiper( + viewportFraction: 0.75, + outer: true, + autoplay: true, + itemCount: 6, + loop: true, + transformer: TDPageTransformer.scaleAndFade(), + pagination: const SwiperPagination( + alignment: Alignment.center, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } + + @Demo(group: 'swiper') + Widget _buildOuterDotsSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + outer: true, + pagination: const SwiperPagination( + alignment: Alignment.bottomCenter, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } + + @Demo(group: 'swiper') + Widget _buildRightDotsSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + scrollDirection: Axis.vertical, + pagination: const SwiperPagination( + alignment: Alignment.centerRight, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } + + @Demo(group: 'swiper') + Widget _buildNotLoopCardsSwiper(BuildContext context) { + return Swiper( + viewportFraction: 0.75, + scale: 0.8, + outer: true, + autoplay: true, + itemCount: 2, + loop: false, + pagination: const SwiperPagination( + alignment: Alignment.center, + builder: TDSwiperPagination.dots), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } + + @Demo(group: 'swiper') + Widget _buildOuterDotsBarSwiper(BuildContext context) { + return Swiper( + outer: true, + autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.topLeft, + builder: TDSwiperPagination.dotsBar), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } + + @Demo(group: 'swiper') + Widget _buildFractionBarSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + pagination: const SwiperPagination( + alignment: Alignment.bottomRight, + builder: TDSwiperPagination.fraction), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } + + @Demo(group: 'swiper') + Widget _buildVerticalDotsBarSwiper(BuildContext context) { + return Swiper( + autoplay: true, + itemCount: 6, + loop: true, + scrollDirection:Axis.vertical, + pagination: const SwiperPagination( + alignment: Alignment.bottomRight, + builder: TDSwiperPagination.dotsBar), + itemBuilder: (BuildContext context, int index) { + return const TDImage(assetUrl: 'assets/img/image.png',); + }, + ); + } +} + diff --git a/tdesign-component/example/lib/page/td_switch_page.dart b/tdesign-component/example/lib/page/td_switch_page.dart new file mode 100644 index 000000000..5f95a37e0 --- /dev/null +++ b/tdesign-component/example/lib/page/td_switch_page.dart @@ -0,0 +1,317 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +/// +/// TdSwitchPage演示 +/// +class TDSwitchPage extends StatefulWidget { + const TDSwitchPage({Key? key}) : super(key: key); + + @override + State createState() { + return TDSwitchPageState(); + } +} + +class TDSwitchPageState extends State { + @override + Widget build(BuildContext context) { + var current = ExamplePage( + title: tdTitle(), + exampleCodeGroup: 'switch', + desc: '用于控制某个功能的开启和关闭。', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '基础开关', builder: _buildSwitchWithBase), + ExampleItem(desc: '带描述开关', builder: _buildSwitchWithText), + ExampleItem(builder: _buildSwitchWithIcon), + ExampleItem(desc: '自定义颜色开关', builder: _buildSwitchWithColor), + ], + ), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '加载状态', builder: _buildSwitchWithLoadingOff), + ExampleItem(builder: _buildSwitchWithLoadingOn), + ExampleItem(desc: '禁用状态', builder: _buildSwitchWithDisableOff), + ExampleItem(builder: _buildSwitchWithDisableOn), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem(desc: '开关尺寸', builder: _buildSwitchWithSizeLarge), + ExampleItem(builder: _buildSwitchWithSizeMed), + ExampleItem(builder: _buildSwitchWithSizeSmall), + ]), + ], + test: [ + ExampleItem(desc: '自定义开关文案-通常只支持一个字符,超出部分无法展示', builder: _customText), + ExampleItem(desc: '自定义带文字开关的字体大小', builder: _customTextFont), + ], + ); + return current; + } + + Widget demoRow( + BuildContext context, + String? title, { + String? desc, + bool on = true, + bool enable = true, + Color? trackOnColor, + Color? trackOffColor, + Color? thumbContentOnColor, + Color? thumbContentOffColor, + TDSwitchSize? size, + TDSwitchType? type, + }) { + final theme = TDTheme.of(context); + Widget current = Row( + children: [ + Expanded( + child: TDText( + title, + textColor: theme.fontGyColor1, + )), + TDText( + desc ?? '', + textColor: theme.grayColor6, + ), + SizedBox( + child: _buildSwitch( + on: on, + enable: enable, + trackOnColor: trackOnColor, + trackOffColor: trackOffColor, + thumbContentOnColor: thumbContentOnColor, + thumbContentOffColor: thumbContentOffColor, + size: size, + type: type), + ) + ], + ); + current = Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: SizedBox( + child: Container( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: current, + ), + color: Colors.white, + ), + height: 56, + ), + ); + return current; + } + + /// 每一项的封装 + @Demo(group: 'switch') + Widget _buildItem(BuildContext context, Widget switchItem, + {String? title, String? desc}) { + final theme = TDTheme.of(context); + Widget current = Row( + children: [ + Expanded( + child: TDText( + title ?? '', + textColor: theme.fontGyColor1, + )), + TDText( + desc ?? '', + textColor: theme.grayColor6, + ), + SizedBox(child: switchItem) + ], + ); + current = Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: SizedBox( + child: Container( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: current, + ), + color: Colors.white, + ), + height: 56, + ), + ); + return Column(mainAxisSize: MainAxisSize.min, children: [current]); + } + + @Demo(group: 'switch') + Widget _buildSwitchWithBase(BuildContext context) { + return _buildItem( + context, + const TDSwitch(), + title: '基础开关', + ); + } + + @Demo(group: 'switch') + Widget _buildSwitchWithText(BuildContext context) { + return _buildItem( + context, + const TDSwitch(isOn: true, type: TDSwitchType.text), + title: '带文字开关', + ); + } + + @Demo(group: 'switch') + Widget _buildSwitchWithIcon(BuildContext context) { + return _buildItem( + context, + const TDSwitch(isOn: true, type: TDSwitchType.icon), + title: '带图标开关', + ); + } + + @Demo(group: 'switch') + Widget _buildSwitchWithColor(BuildContext context) { + return _buildItem( + context, + const TDSwitch(isOn: true, trackOnColor: Colors.green), + title: '自定义颜色开关', + ); + } + + @Demo(group: 'switch') + Widget _buildSwitchWithLoadingOff(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + isOn: false, + type: TDSwitchType.loading, + ), + title: '加载状态', + ); + } + + @Demo(group: 'switch') + Widget _buildSwitchWithLoadingOn(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + isOn: true, + type: TDSwitchType.loading, + ), + title: '加载状态', + ); + } + + @Demo(group: 'switch') + Widget _buildSwitchWithDisableOff(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + enable: false, + isOn: false, + ), + title: '禁用状态', + ); + } + + @Demo(group: 'switch') + Widget _buildSwitchWithDisableOn(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + enable: false, + isOn: true, + ), + title: '禁用状态', + ); + } + + @Demo(group: 'switch') + Widget _buildSwitchWithSizeLarge(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + isOn: true, + size: TDSwitchSize.large, + ), + title: '大尺寸32', + ); + } + + @Demo(group: 'switch') + Widget _buildSwitchWithSizeMed(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + isOn: true, + size: TDSwitchSize.medium, + ), + title: '中尺寸28', + ); + } + + @Demo(group: 'switch') + Widget _buildSwitchWithSizeSmall(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + isOn: true, + size: TDSwitchSize.small, + ), + title: '小尺寸24', + ); + } + + @Demo(group: 'switch') + Widget _buildSwitch({ + bool on = true, + bool enable = true, + Color? trackOnColor, + Color? trackOffColor, + Color? thumbContentOnColor, + Color? thumbContentOffColor, + TDSwitchSize? size, + TDSwitchType? type, + }) { + return TDSwitch( + isOn: on, + trackOnColor: trackOnColor, + trackOffColor: trackOffColor, + thumbContentOnColor: thumbContentOnColor, + thumbContentOffColor: thumbContentOffColor, + enable: enable, + size: size, + type: type, + ); + } + + @Demo(group: 'switch') + Widget _customText(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + type: TDSwitchType.text, + openText: '1111', + closeText: '—', + ), + title: '基础开关', + ); + } + + @Demo(group: 'switch') + Widget _customTextFont(BuildContext context) { + return _buildItem( + context, + const TDSwitch( + type: TDSwitchType.text, + openText: '开', + closeText: '关', + thumbContentOffColor: Colors.red, + thumbContentOnColor: Colors.green, + thumbContentOnFont: TextStyle(fontSize: 18), + thumbContentOffFont: TextStyle(fontSize: 12), + ), + title: '基础开关', + ); + } +} diff --git a/tdesign-component/example/lib/page/td_table_page.dart b/tdesign-component/example/lib/page/td_table_page.dart new file mode 100644 index 000000000..08a1ba6f9 --- /dev/null +++ b/tdesign-component/example/lib/page/td_table_page.dart @@ -0,0 +1,376 @@ +import 'package:flutter/cupertino.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDTablePage extends StatelessWidget { + const TDTablePage({Key? key}) : super(key: key); + + List _getData(int index) { + var data = []; + for (var i = 0; i < 10; i++) { + if (i == index) { + data.add({ + 'title1': '内容内容内容内容', + 'title2': '内容', + 'title3': '内容', + 'title4': '内容', + }); + } else { + data.add({ + 'title1': '内容', + 'title2': '内容', + 'title3': '内容', + 'title4': '内容', + }); + } + } + return data; + } + + List _getData2() { + var data = []; + for (var i = 0; i < 10; i++) { + if (i == 0) { + data.add({ + 'title1': '横向平铺内容不省略', + 'title2': '横向平铺内容不省略', + 'title3': '横向平铺内容不省略', + }); + } else { + data.add({ + 'title1': '内容', + 'title2': '内容', + 'title3': '内容', + }); + } + } + return data; + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(context), + desc: '表格常用于展示同类结构下的多种数据,易于组织、对比和分析等,并可对数据进行搜索、筛选、排序等操作。一般包括表头、数据行和表尾三部分。', + exampleCodeGroup: 'table', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '基础表格', builder: _basicTable), + ExampleItem(desc: '可排序表格', builder: _sortableTable), + ExampleItem(desc: '带操作或按钮表格', builder: _operationBtnTable), + ExampleItem(builder: _operationIconTable, padding: const EdgeInsets.only(top: 16)), + ExampleItem(desc: '可固定首列表格', builder: _fixedFirstColTable), + ExampleItem(desc: '可固定尾列表格', builder: _fixedEndColTable), + ExampleItem(desc: '横向平铺可滚动表格', builder: _horizontalScrollTable), + ], + ), + ExampleModule(title: '组件样式', children: [ + ExampleItem(desc: '带斑马纹表格样式', builder: _stripeTable), + ExampleItem(desc: '带边框表格样式', builder: _borderTable), + ]), + ], + test: [ + ExampleItem(desc: '固定表头', builder: _fixedHeaderTable), + ExampleItem(desc: '固定列尾+滚动表格', builder: _fixedScrollTable), + ExampleItem(desc: '内容居中表格', builder: _centerTable), + ExampleItem(desc: '空数据表格', builder: _emptyTable), + ExampleItem(desc: '加载动画表格', builder: _loadingTable), + ExampleItem(desc: '可选表格+默认选中', builder: _selectTable), + ], + ); + } + + @Demo(group: 'table') + Widget _basicTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + data: _getData(9), + ); + } + + @Demo(group: 'table') + Widget _sortableTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true, sortable: true), + TDTableCol(title: '标题', colKey: 'title2', sortable: true), + TDTableCol(title: '标题', colKey: 'title3', sortable: true), + TDTableCol(title: '标题', colKey: 'title4', sortable: true) + ], + data: _getData(9), + ); + } + + @Demo(group: 'table') + Widget _operationBtnTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol( + title: '标题', + colKey: 'title4', + cellBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TDText( + '修改', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + height: 1, + ), + ), + TDText( + '通过', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + height: 1, + ), + ), + ], + ); + }, + ) + ], + data: _getData(9), + ); + } + + @Demo(group: 'table') + Widget _operationIconTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol( + title: '标题', + colKey: 'title4', + cellBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Icon(TDIcons.upload, color: TDTheme.of(context).brandNormalColor, size: 16), + Icon(TDIcons.delete, color: TDTheme.of(context).brandNormalColor, size: 16), + ], + ); + }, + ) + ], + data: _getData(9), + ); + } + + @Demo(group: 'table') + Widget _fixedFirstColTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1'), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4', fixed: TDTableColFixed.left), + ], + data: _getData(10), + ); + } + + @Demo(group: 'table') + Widget _fixedEndColTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1'), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol( + title: '标题', + colKey: 'title4', + fixed: TDTableColFixed.right, + cellBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TDText( + '修改', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + ), + ), + TDText( + '通过', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + ), + ), + ], + ); + }, + ), + ], + data: _getData(10), + ); + } + + @Demo(group: 'table') + Widget _horizontalScrollTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', width: 160), + TDTableCol(title: '标题', colKey: 'title2', width: 160), + TDTableCol(title: '标题', colKey: 'title3', width: 160), + ], + data: _getData2(), + ); + } + + @Demo(group: 'table') + Widget _stripeTable(BuildContext context) { + return TDTable( + stripe: true, + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + data: _getData(9), + ); + } + + @Demo(group: 'table') + Widget _borderTable(BuildContext context) { + return TDTable( + bordered: true, + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + data: _getData(9), + ); + } + + @Demo(group: 'table') + Widget _fixedHeaderTable(BuildContext context) { + return TDTable( + bordered: true, + height: 240, + columns: [ + TDTableCol(title: '标题', colKey: 'title1', ellipsis: true), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + data: _getData(9), + ); + } + + @Demo(group: 'table') + Widget _fixedScrollTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', width: 200), + TDTableCol(title: '标题', colKey: 'title2', width: 160), + TDTableCol(title: '标题', colKey: 'title3', width: 160), + TDTableCol( + title: '标题', + colKey: 'title4', + fixed: TDTableColFixed.right, + cellBuilder: (BuildContext context, int index) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TDText( + '修改', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + ), + ), + TDText( + '通过', + style: TextStyle( + color: TDTheme.of(context).brandNormalColor, + fontSize: 14, + ), + ), + ], + ); + }, + ), + ], + data: _getData2(), + ); + } + + @Demo(group: 'table') + Widget _centerTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1', align: TDTableColAlign.center), + TDTableCol(title: '标题', colKey: 'title2', align: TDTableColAlign.center), + TDTableCol(title: '标题', colKey: 'title3', align: TDTableColAlign.center), + TDTableCol(title: '标题', colKey: 'title4', align: TDTableColAlign.center) + ], + data: _getData(10), + ); + } + + @Demo(group: 'table') + Widget _emptyTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1'), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + ); + } + + @Demo(group: 'table') + Widget _loadingTable(BuildContext context) { + return TDTable( + columns: [ + TDTableCol(title: '标题', colKey: 'title1'), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + loading: true, + ); + } + + @Demo(group: 'table') + Widget _selectTable(BuildContext context) { + return TDTable( + data: _getData(10), + columns: [ + TDTableCol(selection: true, checked: (index, row) { + return index == 0; + }, width: 50, selectable: (index, row) { + return index % 2 == 0; + }), + TDTableCol(title: '标题', colKey: 'title1'), + TDTableCol(title: '标题', colKey: 'title2'), + TDTableCol(title: '标题', colKey: 'title3'), + TDTableCol(title: '标题', colKey: 'title4') + ], + ); + } +} diff --git a/tdesign-component/example/lib/page/td_tabs_page.dart b/tdesign-component/example/lib/page/td_tabs_page.dart new file mode 100644 index 000000000..5cc901368 --- /dev/null +++ b/tdesign-component/example/lib/page/td_tabs_page.dart @@ -0,0 +1,445 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDTabsPage extends StatefulWidget { + const TDTabsPage({Key? key}) : super(key: key); + + @override + State createState() => _TDTabsPageState(); +} + +class _TDTabsPageState extends State + with TickerProviderStateMixin { + TabController? _tabController1; + TabController? _tabController2; + TabController? _tabController3; + TabController? _tabController4; + List tabs = []; + List tabViews = []; + + List _getTabs() { + tabs = const [ + TDTab( + text: '选项', + ), + TDTab( + text: '选项', + ), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + TDTab(text: '选项'), + ]; + return tabs; + } + + List _getTabViews() { + tabViews = const [ + Center(child: TDText('内容区')), + Center(child: TDText('内容区')), + Center(child: TDText('内容区')), + ]; + return tabViews; + } + + @override + void initState() { + _initTabController(); + _getTabs(); + super.initState(); + } + + List subList(int length) { + var temp = []; + for (var i = 0; i < length; i++) { + temp.add(tabs[i]); + } + switch (length) { + case 3: + temp[temp.length - 1] = const TDTab(text: '上限六个字'); + break; + case 4: + temp[temp.length - 1] = const TDTab(text: '上限四字'); + break; + case 5: + temp[temp.length - 1] = const TDTab(text: '上限三'); + break; + } + return temp; + } + + //初始化tab + void _initTabController() { + _tabController1 = TabController(length: 2, vsync: this); + _tabController2 = TabController(length: 3, vsync: this); + _tabController3 = TabController(length: 4, vsync: this); + _tabController4 = TabController(length: 5, vsync: this); + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '用于内容分类后的展示切换。', + exampleCodeGroup: 'tabs', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '均分选项卡', builder: _buildItemWithSplit1), + ExampleItem(builder: _buildItemWithSplit2, padding: const EdgeInsets.only(top: 16)), + ExampleItem(builder: _buildItemWithSplit3, padding: const EdgeInsets.only(top: 16)), + ExampleItem(builder: _buildItemWithSplit4, padding: const EdgeInsets.only(top: 16)), + ExampleItem(desc: '等距选项卡', builder: _buildItemWithSpace), + ExampleItem(desc: '带图标选项卡', builder: _buildItemWithIcon), + ExampleItem(desc: '带微标选项卡', builder: _buildItemWithLogo), + ExampleItem(desc: '带内容区选项卡', builder: _buildItemWithContent), + ], + ), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '选项卡状态', builder: _buildItemWithStatus), + ]), + ExampleModule(title: '组件样式', children: [ + ExampleItem(desc: '选项卡尺寸', builder: _buildItemWithSizeSmall), + ExampleItem(builder: _buildItemWithSizeBig, padding: const EdgeInsets.only(top: 16)), + ExampleItem(desc: '选项卡样式', builder: _buildItemWithOutlineNormal), + ExampleItem(builder: _buildItemWithOutlineCard, padding: const EdgeInsets.only(top: 16)), + ]), + ], + test: [ + ExampleItem(desc: '自定义下标属性', builder: _customIndicatorStyle), + ExampleItem(desc: '自定义下划线样式', builder: _customDividerStyle), + ExampleItem(desc: '不展示下划线-高度为0', builder: _hideBottomDivider), + ExampleItem(desc: 'capsule类型可修改背景色', builder: _capsuleBackgroundColor), + ],); + } + + @Demo(group: 'tabs') + Widget _buildItemWithSplit1(BuildContext context) { + return TDTabBar( + tabs: subList(2), + controller: _tabController1, + backgroundColor: Colors.white, + showIndicator: true, + ); + } + + @Demo(group: 'tabs') + Widget _buildItemWithSplit2(BuildContext context) { + return TDTabBar( + tabs: subList(3), + controller: _tabController2, + backgroundColor: Colors.white, + showIndicator: true, + ); + } + + @Demo(group: 'tabs') + Widget _buildItemWithSplit3(BuildContext context) { + return TDTabBar( + tabs: subList(4), + controller: _tabController3, + backgroundColor: Colors.white, + showIndicator: true, + ); + } + + @Demo(group: 'tabs') + Widget _buildItemWithSplit4(BuildContext context) { + return TDTabBar( + tabs: subList(5), + controller: _tabController4, + backgroundColor: Colors.white, + showIndicator: true, + ); + } + + @Demo(group: 'tabs') + Widget _buildItemWithSpace(BuildContext context) { + return TDTabBar( + tabs: subList(16), + controller: TabController(length: 16, vsync: this), + backgroundColor: Colors.white, + labelPadding: const EdgeInsets.all(10), + showIndicator: true, + isScrollable: true, + ); + } + + @Demo(group: 'tabs') + Widget _buildItemWithIcon(BuildContext context) { + var tabs = [ + const TDTab( + text: '选项', + icon: Icon( + TDIcons.app, + size: 18, + ), + ), + const TDTab( + text: '选项', + icon: Icon( + TDIcons.app, + size: 18, + ), + ), + const TDTab( + text: '选项', + icon: Icon( + TDIcons.app, + size: 18, + ), + ), + ]; + return TDTabBar( + tabs: tabs, + controller: TabController(length: 3, vsync: this), + backgroundColor: Colors.white, + showIndicator: true); + } + + @Demo(group: 'tabs') + Widget _buildItemWithLogo(BuildContext context) { + var tabs = [ + const TDTab( + text: '选项', + contentHeight: 48, + textMargin: EdgeInsets.only(right: 8), + badge: TDBadge(TDBadgeType.redPoint), + ), + const TDTab( + text: '选项', + contentHeight: 42, + textMargin: EdgeInsets.only(right: 16, top: 2, bottom: 2), + badge: TDBadge( + TDBadgeType.message, + message: '8', + ), + ), + const TDTab( + text: '选项', + height: 48, + icon: Icon( + TDIcons.app, + size: 18, + ), + ), + ]; + return TDTabBar( + tabs: tabs, + controller: TabController(length: 3, vsync: this), + backgroundColor: Colors.white, + showIndicator: true); + } + + @Demo(group: 'tabs') + Widget _buildItemWithContent(BuildContext context) { + var tabController = TabController(length: 3, vsync: this); + return SizedBox( + height: 120 + 48, + child: Column( + children: [ + TDTabBar( + tabs: subList(3), + controller: tabController, + showIndicator: true, + backgroundColor: Colors.white, + isScrollable: false,), + Container( + height: 120, + color: Colors.white, + child: TDTabBarView( + children: _getTabViews(), + controller: tabController, + ), + ) + ], + ), + ); + } + + @Demo(group: 'tabs') + Widget _buildItemWithStatus(BuildContext context) { + var tabs = [ + const TDTab( + text: '选中', + ), + const TDTab( + text: '默认', + ), + const TDTab( + text: '禁用', + enable: false, + ), + ]; + return TDTabBar( + tabs: tabs, + controller: TabController(length: 3, vsync: this), + backgroundColor: Colors.white, + showIndicator: true); + } + + @Demo(group: 'tabs') + Widget _buildItemWithSizeSmall(BuildContext context) { + var tabs = [ + const TDTab( + text: '小尺寸', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + ]; + return TDTabBar( + tabs: tabs, + controller: TabController(length: 4, vsync: this), + backgroundColor: Colors.white, + showIndicator: true); + } + + @Demo(group: 'tabs') + Widget _buildItemWithSizeBig(BuildContext context) { + var tabs = [ + const TDTab( + text: '大尺寸', + size: TDTabSize.large, + ), + const TDTab( + text: '选项', + size: TDTabSize.large, + ), + const TDTab( + text: '选项', + size: TDTabSize.large, + ), + const TDTab( + text: '选项', + size: TDTabSize.large, + ), + ]; + return TDTabBar( + tabs: tabs, + controller: TabController(length: 4, vsync: this), + backgroundColor: Colors.white, + showIndicator: true); + } + + @Demo(group: 'tabs') + Widget _buildItemWithOutlineNormal(BuildContext context) { + var tabs = [ + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + ]; + return TDTabBar( + tabs: tabs, + outlineType: TDTabBarOutlineType.capsule, + controller: TabController(length: 4, vsync: this), + backgroundColor: Colors.white, + showIndicator: false); + } + + @Demo(group: 'tabs') + Widget _buildItemWithOutlineCard(BuildContext context) { + var tabs = [ + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + const TDTab( + text: '选项', + ), + ]; + return TDTabBar( + tabs: tabs, + outlineType: TDTabBarOutlineType.card, + controller: TabController(length: 4, vsync: this), + backgroundColor: Colors.white, + showIndicator: false); + } + + @Demo(group: 'tabs') + Widget _customIndicatorStyle(BuildContext context) { + return TDTabBar( + tabs: subList(2), + controller: _tabController1, + backgroundColor: Colors.white, + showIndicator: true, + indicatorColor: Colors.red, + indicatorHeight: 20, + indicatorWidth: 10, + indicatorPadding: const EdgeInsets.only(left: 20), + ); + } + + @Demo(group: 'tabs') + Widget _customDividerStyle(BuildContext context) { + return TDTabBar( + tabs: subList(2), + controller: _tabController1, + backgroundColor: Colors.white, + showIndicator: true, + dividerColor: Colors.red, + dividerHeight: 5, + ); + } + + @Demo(group: 'tabs') + Widget _hideBottomDivider(BuildContext context) { + return TDTabBar( + tabs: subList(2), + controller: _tabController1, + backgroundColor: Colors.white, + showIndicator: true, + dividerColor: Colors.red, + dividerHeight: 0, + ); + } + + @Demo(group: 'tabs') + Widget _capsuleBackgroundColor(BuildContext context) { + return TDTabBar( + tabs: subList(2), + controller: _tabController1, + backgroundColor: Colors.red, + outlineType: TDTabBarOutlineType.capsule, + ); + } +} diff --git a/tdesign-component/example/lib/page/td_tag_page.dart b/tdesign-component/example/lib/page/td_tag_page.dart new file mode 100644 index 000000000..a0e00620b --- /dev/null +++ b/tdesign-component/example/lib/page/td_tag_page.dart @@ -0,0 +1,1201 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDTagPage extends StatelessWidget { + const TDTagPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(context), + desc: '用于表明主体的类目,属性或状态', + exampleCodeGroup: 'tag', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + desc: '基础标签', + ignoreCode: true, + builder: (context) { + return Row( + children: [ + const SizedBox( + width: 16, + ), + CodeWrapper(builder: _buildSimpleFillTag), + const SizedBox( + width: 16, + ), + CodeWrapper(builder: _buildSimpleOutlineTag), + ], + ); + }), + ExampleItem( + desc: '圆弧标签', + ignoreCode: true, + builder: (context) { + return Row( + children: [ + const SizedBox( + width: 16, + ), + CodeWrapper(builder: _buildCircleFillTag), + const SizedBox( + width: 16, + ), + CodeWrapper(builder: _buildCircleOutlineTag), + ], + ); + }), + ExampleItem( + desc: 'Mark标签', + ignoreCode: true, + builder: (context) { + return Row( + children: [ + const SizedBox( + width: 16, + ), + CodeWrapper(builder: _buildMarkFillTag), + const SizedBox( + width: 16, + ), + CodeWrapper(builder: _buildMarkOutlineTag), + ], + ); + }), + ExampleItem( + desc: '带图标的标签', + ignoreCode: true, + builder: (context) { + return Row( + children: [ + const SizedBox( + width: 16, + ), + CodeWrapper(builder: _buildIconFillTag), + const SizedBox( + width: 16, + ), + CodeWrapper(builder: _buildIconOutlineTag), + ], + ); + }), + ExampleItem( + desc: '可关闭的标签', + ignoreCode: true, + builder: (context) { + return Row( + children: [ + const SizedBox( + width: 16, + ), + CodeWrapper(builder: _buildCloseFillTag), + const SizedBox( + width: 16, + ), + CodeWrapper(builder: _buildCloseOutlineTag), + ], + ); + }), + ExampleItem( + desc: '可选中的标签', + ignoreCode: true, + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 16), + child: Wrap(spacing: 8, direction: Axis.vertical, children: [ + // 非浅色填充 + Row( + children: [ + const SizedBox( + width: 80, + child: TDText('dark'), + ), + CodeWrapper(builder: _buildDarkSelectTags) + ], + ), + // 浅色填充 + Row( + children: [ + const SizedBox( + width: 80, + child: TDText('light'), + ), + CodeWrapper(builder: _buildLightSelectTags) + ], + ), + // 非浅色描边 + Row( + children: [ + const SizedBox( + width: 80, + child: TDText('outline'), + ), + CodeWrapper(builder: _buildOutlineSelectTags) + ], + ), + // 浅色描边 + Row( + children: [ + const SizedBox( + width: 80, + child: TDText('light-outline'), + ), + CodeWrapper(builder: _buildLightOutlineSelectTags) + ], + ), + ]), + ); + }), + ]), + ExampleModule(title: '组件状态(主题)', children: [ + ExampleItem( + desc: '展示型标签', + ignoreCode: true, + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 16), + child: Wrap( + spacing: 8, + direction: Axis.vertical, + children: [ + // 浅色填充 + CodeWrapper(builder: _buildLightShowTags), + + // 非浅色填充 + CodeWrapper(builder: _buildDarkShowTags), + + // 非浅色描边 + CodeWrapper(builder: _buildOutlineShowTags), + + // 浅色描边 + CodeWrapper(builder: _buildLightOutlineShowTags), + ], + ), + ); + }), + ]), + ExampleModule(title: '组件尺寸', children: [ + ExampleItem( + ignoreCode: true, + builder: (context) { + return Container( + alignment: Alignment.topLeft, + padding: const EdgeInsets.only(left: 16), + child: Wrap(spacing: 8, direction: Axis.vertical, children: [ + // 不带关闭 + CodeWrapper(builder: _buildAllSizeTags), + // 带关闭 + CodeWrapper(builder: _buildAllSizeCloseTags), + ]), + ); + }) + ]) + ], + test: [ + ExampleItem( + desc: '非浅色填充的各主题展示', + ignoreCode: true, + builder: (context) { + return Wrap( + spacing: 8, + children: const [ + TDTag( + '标签文字', + ), + TDTag( + '标签文字', + theme: TDTagTheme.primary, + ), + TDTag( + '标签文字', + theme: TDTagTheme.warning, + ), + TDTag( + '标签文字', + theme: TDTagTheme.danger, + ), + TDTag( + '标签文字', + theme: TDTagTheme.success, + ), + ], + ); + }), + ExampleItem( + desc: '浅色填充的各主题展示', + ignoreCode: true, + builder: (context) { + return Wrap( + spacing: 8, + children: const [ + TDTag( + '标签文字', + isLight: true, + ), + TDTag( + '标签文字', + isLight: true, + theme: TDTagTheme.primary, + ), + TDTag( + '标签文字', + isLight: true, + theme: TDTagTheme.warning, + ), + TDTag( + '标签文字', + isLight: true, + theme: TDTagTheme.danger, + ), + TDTag( + '标签文字', + isLight: true, + theme: TDTagTheme.success, + ), + ], + ); + }), + ExampleItem( + desc: '非浅色描边的各主题展示', + ignoreCode: true, + builder: (context) { + return Wrap( + spacing: 8, + children: const [ + TDTag( + '标签文字', + isOutline: true, + ), + TDTag( + '标签文字', + isOutline: true, + theme: TDTagTheme.primary, + ), + TDTag( + '标签文字', + isOutline: true, + theme: TDTagTheme.warning, + ), + TDTag( + '标签文字', + isOutline: true, + theme: TDTagTheme.danger, + ), + TDTag( + '标签文字', + isOutline: true, + theme: TDTagTheme.success, + ), + ], + ); + }), + ExampleItem( + desc: '浅色描边的各主题展示', + ignoreCode: true, + builder: (context) { + return Wrap( + spacing: 8, + children: const [ + TDTag( + '标签文字', + isOutline: true, + isLight: true, + ), + TDTag( + '标签文字', + isOutline: true, + isLight: true, + theme: TDTagTheme.primary, + ), + TDTag( + '标签文字', + isOutline: true, + isLight: true, + theme: TDTagTheme.warning, + ), + TDTag( + '标签文字', + isOutline: true, + isLight: true, + theme: TDTagTheme.danger, + ), + TDTag( + '标签文字', + isOutline: true, + isLight: true, + theme: TDTagTheme.success, + ), + ], + ); + }), + ExampleItem( + desc: '各主题关闭图标颜色不会变', + ignoreCode: true, + builder: (context) { + return Wrap( + spacing: 8, + runSpacing: 8, + children: const [ + TDTag( + '标签文字', + isOutline: true, + needCloseIcon: true, + ), + TDTag( + '标签文字', + isOutline: true, + needCloseIcon: true, + theme: TDTagTheme.primary, + ), + TDTag( + '标签文字', + isOutline: true, + needCloseIcon: true, + theme: TDTagTheme.warning, + ), + TDTag( + '标签文字', + isOutline: true, + needCloseIcon: true, + theme: TDTagTheme.danger, + ), + TDTag( + '标签文字', + isOutline: true, + needCloseIcon: true, + theme: TDTagTheme.success, + ), + TDTag( + '标签文字', + needCloseIcon: true, + ), + TDTag( + '标签文字', + needCloseIcon: true, + theme: TDTagTheme.primary, + ), + TDTag( + '标签文字', + needCloseIcon: true, + theme: TDTagTheme.warning, + ), + TDTag( + '标签文字', + needCloseIcon: true, + theme: TDTagTheme.danger, + ), + TDTag( + '标签文字', + needCloseIcon: true, + theme: TDTagTheme.success, + ), + ], + ); + }), + ExampleItem( + desc: '带图标可关闭的标签', + ignoreCode: true, + builder: (context) { + return Row( + children: const [ + SizedBox( + width: 16, + ), + TDTag( + '标签文字', + icon: TDIcons.discount, + needCloseIcon: true, + ), + SizedBox( + width: 16, + ), + TDTag( + '标签文字', + icon: TDIcons.discount, + needCloseIcon: true, + isOutline: true, + ), + ], + ); + }), + ExampleItem( + desc: '各尺寸测试', + ignoreCode: true, + builder: (context) { + return Wrap(spacing: 8, direction: Axis.vertical, children: [ + // 带图标和关闭 + Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(left: 16, right: 16), + child: Wrap(spacing: 8, runSpacing: 8, children: const [ + TDTag( + '加大尺寸', + icon: TDIcons.discount, + needCloseIcon: true, + size: TDTagSize.extraLarge, + ), + TDTag( + '大尺寸', + icon: TDIcons.discount, + needCloseIcon: true, + size: TDTagSize.large, + ), + TDTag( + '中尺寸', + icon: TDIcons.discount, + needCloseIcon: true, + size: TDTagSize.medium, + ), + TDTag( + '小尺寸', + icon: TDIcons.discount, + needCloseIcon: true, + size: TDTagSize.small, + ), + ]), + ), + // 带图标和关闭,描边 + Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(left: 16, right: 16), + child: Wrap(spacing: 8, runSpacing: 8, children: const [ + TDTag( + '加大尺寸', + isOutline: true, + icon: TDIcons.discount, + needCloseIcon: true, + size: TDTagSize.extraLarge, + ), + TDTag( + '大尺寸', + isOutline: true, + icon: TDIcons.discount, + needCloseIcon: true, + size: TDTagSize.large, + ), + TDTag( + '中尺寸', + isOutline: true, + icon: TDIcons.discount, + needCloseIcon: true, + size: TDTagSize.medium, + ), + TDTag( + '小尺寸', + isOutline: true, + icon: TDIcons.discount, + needCloseIcon: true, + size: TDTagSize.small, + ), + ]), + ), + ]); + }), + ExampleItem( + desc: '可选各状态测试', + ignoreCode: true, + builder: (context) { + return Wrap(spacing: 8, direction: Axis.vertical, children: [ + // Normal + Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(left: 16, right: 16), + child: Wrap(spacing: 8, runSpacing: 8, children: const [ + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + icon: TDIcons.discount, + needCloseIcon: true, + shape: TDTagShape.mark, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isSelected: true, + shape: TDTagShape.mark, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + icon: TDIcons.discount, + needCloseIcon: true, + isSelected: true, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + disableSelect: true, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + icon: TDIcons.discount, + needCloseIcon: true, + disableSelect: true, + shape: TDTagShape.mark, + ), + ]), + ), + // Light + Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(left: 16, right: 16), + child: Wrap(spacing: 8, runSpacing: 8, children: const [ + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isLight: true, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isLight: true, + icon: TDIcons.discount, + needCloseIcon: true, + shape: TDTagShape.mark, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isLight: true, + isSelected: true, + shape: TDTagShape.mark, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + icon: TDIcons.discount, + isLight: true, + needCloseIcon: true, + isSelected: true, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isLight: true, + disableSelect: true, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isLight: true, + icon: TDIcons.discount, + needCloseIcon: true, + disableSelect: true, + shape: TDTagShape.mark, + ), + ]), + ), + // Outline + Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(left: 16, right: 16), + child: Wrap(spacing: 8, runSpacing: 8, children: const [ + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + icon: TDIcons.discount, + needCloseIcon: true, + shape: TDTagShape.mark, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + isSelected: true, + shape: TDTagShape.mark, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + icon: TDIcons.discount, + isOutline: true, + needCloseIcon: true, + isSelected: true, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + disableSelect: true, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + icon: TDIcons.discount, + needCloseIcon: true, + disableSelect: true, + shape: TDTagShape.mark, + ), + ]), + ), + // Outline-Light + Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(left: 16, right: 16), + child: Wrap(spacing: 8, runSpacing: 8, children: const [ + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + icon: TDIcons.discount, + needCloseIcon: true, + shape: TDTagShape.mark, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + isSelected: true, + shape: TDTagShape.mark, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + icon: TDIcons.discount, + isOutline: true, + isLight: true, + needCloseIcon: true, + isSelected: true, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + disableSelect: true, + ), + TDSelectTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + icon: TDIcons.discount, + needCloseIcon: true, + disableSelect: true, + shape: TDTagShape.mark, + ), + ]), + ), + ]); + }), + ExampleItem( + desc: '展示各状态测试', + ignoreCode: true, + builder: (context) { + return Wrap(spacing: 8, direction: Axis.vertical, children: [ + // Normal + Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(left: 16, right: 16), + child: Wrap(spacing: 8, runSpacing: 8, children: const [ + TDTag( + 'Tag', + theme: TDTagTheme.primary, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + icon: TDIcons.discount, + needCloseIcon: true, + shape: TDTagShape.mark, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + shape: TDTagShape.round, + disable: true, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + icon: TDIcons.discount, + needCloseIcon: true, + disable: true, + shape: TDTagShape.mark, + ), + ]), + ), + // Light + Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(left: 16, right: 16), + child: Wrap(spacing: 8, runSpacing: 8, children: const [ + TDTag( + 'Tag', + theme: TDTagTheme.primary, + isLight: true, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + isLight: true, + icon: TDIcons.discount, + needCloseIcon: true, + shape: TDTagShape.mark, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + shape: TDTagShape.round, + isLight: true, + disable: true, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + isLight: true, + icon: TDIcons.discount, + needCloseIcon: true, + disable: true, + shape: TDTagShape.mark, + ), + ]), + ), + // Outline + Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(left: 16, right: 16), + child: Wrap(spacing: 8, runSpacing: 8, children: const [ + TDTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + icon: TDIcons.discount, + needCloseIcon: true, + shape: TDTagShape.mark, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + shape: TDTagShape.round, + isOutline: true, + disable: true, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + icon: TDIcons.discount, + needCloseIcon: true, + disable: true, + shape: TDTagShape.mark, + ), + ]), + ), + // Outline-Light + Container( + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(left: 16, right: 16), + child: Wrap(spacing: 8, runSpacing: 8, children: const [ + TDTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + icon: TDIcons.discount, + needCloseIcon: true, + shape: TDTagShape.mark, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + shape: TDTagShape.round, + isOutline: true, + isLight: true, + disable: true, + ), + TDTag( + 'Tag', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + icon: TDIcons.discount, + needCloseIcon: true, + disable: true, + shape: TDTagShape.mark, + ), + ]), + ), + ]); + }), + ]); + } + + @Demo(group: 'tag') + TDTag _buildSimpleOutlineTag(BuildContext context) { + return const TDTag( + '标签文字', + isOutline: true, + ); + } + + @Demo(group: 'tag') + TDTag _buildSimpleFillTag(BuildContext context) { + return const TDTag('标签文字'); + } + + @Demo(group: 'tag') + Widget _buildCircleFillTag(BuildContext context) { + return const TDTag( + '标签文字', + shape: TDTagShape.round, + ); + } + + @Demo(group: 'tag') + Widget _buildCircleOutlineTag(BuildContext context) { + return const TDTag( + '标签文字', + shape: TDTagShape.round, + isOutline: true, + ); + } + + @Demo(group: 'tag') + Widget _buildMarkFillTag(BuildContext context) { + return const TDTag( + '标签文字', + shape: TDTagShape.mark, + ); + } + + @Demo(group: 'tag') + Widget _buildMarkOutlineTag(BuildContext context) { + return const TDTag( + '标签文字', + shape: TDTagShape.mark, + isOutline: true, + ); + } + + @Demo(group: 'tag') + Widget _buildIconFillTag(BuildContext context) { + return const TDTag( + '标签文字', + icon: TDIcons.discount, + ); + } + + @Demo(group: 'tag') + Widget _buildIconOutlineTag(BuildContext context) { + return const TDTag( + '标签文字', + icon: TDIcons.discount, + isOutline: true, + ); + } + + @Demo(group: 'tag') + Widget _buildCloseFillTag(BuildContext context) { + return TDTag( + '标签文字', + needCloseIcon: true, + onCloseTap: () { + TDToast.showText('点击关闭', context: context); + }, + ); + } + + @Demo(group: 'tag') + Widget _buildCloseOutlineTag(BuildContext context) { + return TDTag('标签文字', needCloseIcon: true, isOutline: true, onCloseTap: () { + TDToast.showText('点击关闭', context: context); + }); + } + + @Demo(group: 'tag') + Widget _buildDarkSelectTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDSelectTag( + '未选中态', + theme: TDTagTheme.primary, + ), + TDSelectTag( + '已选中态', + theme: TDTagTheme.primary, + isSelected: true, + ), + TDSelectTag( + '不可选态', + theme: TDTagTheme.primary, + disableSelect: true, + ), + ]); + } + + @Demo(group: 'tag') + Widget _buildLightSelectTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDSelectTag( + '未选中态', + theme: TDTagTheme.primary, + isLight: true, + ), + TDSelectTag( + '已选中态', + theme: TDTagTheme.primary, + isLight: true, + isSelected: true, + ), + TDSelectTag( + '不可选态', + theme: TDTagTheme.primary, + isLight: true, + disableSelect: true, + ), + ]); + } + + @Demo(group: 'tag') + Widget _buildOutlineSelectTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDSelectTag( + '未选中态', + theme: TDTagTheme.primary, + isOutline: true, + ), + TDSelectTag( + '已选中态', + theme: TDTagTheme.primary, + isOutline: true, + isSelected: true, + ), + TDSelectTag( + '不可选态', + theme: TDTagTheme.primary, + isOutline: true, + disableSelect: true, + ), + ]); + } + + @Demo(group: 'tag') + Widget _buildLightOutlineSelectTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDSelectTag( + '未选中态', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + ), + TDSelectTag( + '已选中态', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + isSelected: true, + ), + TDSelectTag( + '不可选态', + theme: TDTagTheme.primary, + isOutline: true, + isLight: true, + disableSelect: true, + ), + ]); + } + + @Demo(group: 'tag') + Widget _buildLightShowTags(BuildContext context) { + return Wrap( + spacing: 8, + children: const [ + TDTag('默认', isLight: true), + TDTag( + '主要', + isLight: true, + theme: TDTagTheme.primary, + ), + TDTag( + '警告', + isLight: true, + theme: TDTagTheme.warning, + ), + TDTag( + '危险', + isLight: true, + theme: TDTagTheme.danger, + ), + TDTag( + '成功', + isLight: true, + theme: TDTagTheme.success, + ), + ], + ); + } + + @Demo(group: 'tag') + Widget _buildDarkShowTags(BuildContext context) { + return Wrap( + spacing: 8, + children: const [ + TDTag('默认'), + TDTag( + '主要', + theme: TDTagTheme.primary, + ), + TDTag( + '警告', + theme: TDTagTheme.warning, + ), + TDTag( + '危险', + theme: TDTagTheme.danger, + ), + TDTag( + '成功', + theme: TDTagTheme.success, + ), + ], + ); + } + + @Demo(group: 'tag') + Widget _buildOutlineShowTags(BuildContext context) { + return Wrap( + spacing: 8, + children: const [ + TDTag('默认', isOutline: true), + TDTag( + '主要', + isOutline: true, + theme: TDTagTheme.primary, + ), + TDTag( + '警告', + isOutline: true, + theme: TDTagTheme.warning, + ), + TDTag( + '危险', + isOutline: true, + theme: TDTagTheme.danger, + ), + TDTag( + '成功', + isOutline: true, + theme: TDTagTheme.success, + ), + ], + ); + } + + @Demo(group: 'tag') + Widget _buildLightOutlineShowTags(BuildContext context) { + return Wrap( + spacing: 8, + children: const [ + TDTag('默认', isOutline: true, isLight: true), + TDTag( + '主要', + isOutline: true, + isLight: true, + theme: TDTagTheme.primary, + ), + TDTag( + '警告', + isOutline: true, + isLight: true, + theme: TDTagTheme.warning, + ), + TDTag( + '危险', + isOutline: true, + isLight: true, + theme: TDTagTheme.danger, + ), + TDTag( + '成功', + isOutline: true, + isLight: true, + theme: TDTagTheme.success, + ), + ], + ); + } + + @Demo(group: 'tag') + Widget _buildAllSizeTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDTag( + '加大尺寸', + size: TDTagSize.extraLarge, + ), + TDTag( + '大尺寸', + size: TDTagSize.large, + ), + TDTag( + '中尺寸', + size: TDTagSize.medium, + ), + TDTag( + '小尺寸', + size: TDTagSize.small, + ), + ]); + } + + @Demo(group: 'tag') + Widget _buildAllSizeCloseTags(BuildContext context) { + return Wrap(spacing: 8, children: const [ + TDTag( + '加大尺寸', + needCloseIcon: true, + size: TDTagSize.extraLarge, + ), + TDTag( + '大尺寸', + needCloseIcon: true, + size: TDTagSize.large, + ), + TDTag( + '中尺寸', + needCloseIcon: true, + size: TDTagSize.medium, + ), + TDTag( + '小尺寸', + needCloseIcon: true, + size: TDTagSize.small, + ), + ]); + } +} diff --git a/tdesign-component/example/lib/page/td_text_page.dart b/tdesign-component/example/lib/page/td_text_page.dart new file mode 100644 index 000000000..881caaf09 --- /dev/null +++ b/tdesign-component/example/lib/page/td_text_page.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../annotation/demo.dart'; +import '../../base/example_widget.dart'; + +class TDTextPage extends StatelessWidget { + const TDTextPage({Key? key}) : super(key: key); + + final exampleTxt = '文本Text'; + + @override + Widget build(BuildContext context) { + // debugPaintBaselinesEnabled = true; + return ExamplePage( + padding: const EdgeInsets.all(8), + title: tdTitle(context), + exampleCodeGroup: 'text', + children: [ + ExampleModule(title: '使用示例', children: [ + ExampleItem(desc: '系统Text:', builder: _buildSystemText), + ExampleItem(desc: '普通TDText:', builder: _buildNormalTDText), + ExampleItem(desc: '指定常用属性:', builder: _buildGeneralProp), + ExampleItem( + desc: 'style覆盖textColor,不覆盖font:', + builder: _buildStyleCoverColor), + ExampleItem( + desc: 'style覆盖textColor和font:', + builder: _buildStyleCoverColorAndFont), + ExampleItem(desc: 'TDText.rich测试:', builder: _buildRichText), + ExampleItem(desc: '获取系统Text:', builder: _getSystemText), + ExampleItem( + desc: '中文居中:(带有英文可能不居中)', builder: _buildVerticalCenterText), + ExampleItem(desc: '自定义内部padding:', builder: _buildCustomPaddingText), + ]), + ], + test: [ + ExampleItem( + desc: '中文居中-系统字体', + builder: (context){ + return Container( + color: TDTheme.of(context).brandFocusColor, + child: Text(exampleTxt), + ); + }), + ExampleItem( + desc: '中文居中-TD字体', + builder: (context){ + return Container( + color: TDTheme.of(context).brandFocusColor, + child: TDText(exampleTxt, forceVerticalCenter: true,), + ); + }), + ], + ); + } + + @Demo(group: 'text') + Widget _buildNormalTDText(BuildContext context) { + return TDText( + exampleTxt, + ); + } + + @Demo(group: 'text') + Widget _buildSystemText(BuildContext context) { + return Text( + exampleTxt, + ); + } + + @Demo(group: 'text') + Widget _buildGeneralProp(BuildContext context) { + return TDText( + exampleTxt, + font: TDTheme.of(context).fontHeadlineLarge, + textColor: TDTheme.of(context).brandNormalColor, + backgroundColor: TDTheme.of(context).brandFocusColor, + ); + } + + @Demo(group: 'text') + Widget _buildStyleCoverColor(BuildContext context) { + return TDText( + exampleTxt, + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).brandNormalColor, + style: TextStyle(color: TDTheme.of(context).errorNormalColor), + ); + } + + @Demo(group: 'text') + Widget _buildStyleCoverColorAndFont(BuildContext context) { + return TDText( + exampleTxt, + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).brandNormalColor, + ); + } + + @Demo(group: 'text') + Widget _buildRichText(BuildContext context) { + return TDText.rich( + TextSpan(children: [ + TDTextSpan( + text: 'TDTextSpan1', + font: TDTheme.of(context).fontTitleExtraLarge, + textColor: TDTheme.of(context).warningNormalColor, + isTextThrough: true, + lineThroughColor: TDTheme.of(context).brandNormalColor, + style: TextStyle(color: TDTheme.of(context).errorNormalColor)), + TextSpan( + text: 'TextSpan2', + style: TextStyle( + fontSize: 14, color: TDTheme.of(context).brandNormalColor)), + const WidgetSpan( + child: Icon( + TDIcons.setting, + size: 24, + )), + ]), + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).brandNormalColor, + style: + TextStyle(color: TDTheme.of(context).errorNormalColor, fontSize: 32), + ); + } + + @Demo(group: 'text') + Widget _getSystemText(BuildContext context) { + return TDText( + exampleTxt, + backgroundColor: TDTheme.of(context).brandFocusColor, + ).getRawText(context: context); + } + + @Demo(group: 'text') + Widget _buildVerticalCenterText(BuildContext context) { + return TDText( + '中华人民共和国腾讯科技', + // font: Font(size: 100, lineHeight: 100), + forceVerticalCenter: true, + backgroundColor: TDTheme.of(context).brandFocusColor, + ); + } + + @Demo(group: 'text') + Widget _buildCustomPaddingText(BuildContext context) { + return TDTextConfiguration( + paddingConfig: CustomTextPaddingConfig(), + child: const CustomPaddingText(), + ); + } +} + +/// 自定义控件,内部的context可拿到外部TDTextConfiguration的配置信息 +class CustomPaddingText extends StatelessWidget { + const CustomPaddingText({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + TDText( + '中华人民共和国腾讯科技fgjpqy', + forceVerticalCenter: true, + backgroundColor: TDTheme.of(context).brandFocusColor, + ), + TDText( + 'English', + font: TDTheme.of(context).fontHeadlineLarge, + forceVerticalCenter: true, + backgroundColor: TDTheme.of(context).brandFocusColor, + ), + ], + ); + } +} + +/// 重写内部padding方法 +class CustomTextPaddingConfig extends TDTextPaddingConfig { + @override + EdgeInsetsGeometry getPadding(String? data, double fontSize, double height) { + var supperPadding = super.getPadding(data, fontSize, height); + return EdgeInsets.only(left: 30, top: supperPadding.vertical.toDouble()); + } +} diff --git a/tdesign-component/example/lib/page/td_textarea_page.dart b/tdesign-component/example/lib/page/td_textarea_page.dart new file mode 100644 index 000000000..84b8fb60c --- /dev/null +++ b/tdesign-component/example/lib/page/td_textarea_page.dart @@ -0,0 +1,270 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDTextareaPage extends StatefulWidget { + const TDTextareaPage({Key? key}) : super(key: key); + + @override + _TDTextareaPageState createState() => _TDTextareaPageState(); +} + +class _TDTextareaPageState extends State { + var controller = []; + + @override + void initState() { + for (var i = 0; i < 20; i++) { + controller.add(TextEditingController()); + } + super.initState(); + } + + @override + void dispose() { + controller.forEach((element) { + element.dispose(); + }); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + backgroundColor: const Color(0xFFF0F2F5), + title: tdTitle(), + desc: '用于多行文本信息输入。', + exampleCodeGroup: 'textarea', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '基础多文本输入框', builder: _basicType), + ExampleItem(desc: '带标题多文本输入框', builder: _basicTypeByTitle), + ExampleItem(desc: '自动增高多文本输入框', builder: _autoHeightType), + ExampleItem(desc: '设置字符数限制', builder: _maxLengthType), + ], + ), + ExampleModule( + title: '组件状态', + children: [ + ExampleItem(desc: '禁用状态', builder: _disabledState), + ], + ), + ExampleModule( + title: '组件样式', + children: [ + ExampleItem(desc: '竖排样式', builder: _verticalStyle), + ExampleItem(desc: '卡片样式', builder: _cardStyle), + ], + ), + ExampleModule( + title: '特殊样式', + children: [ + ExampleItem(desc: '标签外置输入框', builder: _extensionStyle), + ExampleItem(desc: '自定义标题', builder: _setLabel), + ExampleItem(desc: '必填和辅助说明', builder: _setStatus), + ], + ), + ], + test: [ + ExampleItem(desc: '自定义宽度', center: false, builder: _setWidth), + ExampleItem(desc: '小尺寸', builder: _smallSize), + ], + ); + } + + @Demo(group: 'textarea') + Widget _basicType(BuildContext context) { + return TDTextarea( + controller: controller[0], + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + onChanged: (value) { + setState(() {}); + }, + ); + } + + @Demo(group: 'textarea') + Widget _basicTypeByTitle(BuildContext context) { + return TDTextarea( + controller: controller[1], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + onChanged: (value) { + }, + ); + } + + @Demo(group: 'textarea') + Widget _autoHeightType(BuildContext context) { + return TDTextarea( + controller: controller[2], + hintText: '请输入文字', + minLines: 1, + onChanged: (value) { + }, + ); + } + + @Demo(group: 'textarea') + Widget _maxLengthType(BuildContext context) { + return TDTextarea( + controller: controller[3], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + onChanged: (value) { + }, + ); + } + + @Demo(group: 'textarea') + Widget _disabledState(BuildContext context) { + return TDTextarea( + controller: controller[4], + label: '标签文字', + hintText: '不可编辑文字', + maxLines: 4, + minLines: 4, + readOnly: true, + onChanged: (value) { + + }, + ); + } + + @Demo(group: 'textarea') + Widget _verticalStyle(BuildContext context) { + return TDTextarea( + controller: controller[5], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + layout: TDTextareaLayout.vertical, + onChanged: (value) { + }, + ); + } + + @Demo(group: 'textarea') + Widget _cardStyle(BuildContext context) { + return TDTextarea( + controller: controller[6], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusExtraLarge), + ), + margin: EdgeInsets.only(right: TDTheme.of(context).spacer16, left: TDTheme.of(context).spacer16), + onChanged: (value) { + }, + ); + } + + @Demo(group: 'textarea') + Widget _extensionStyle(BuildContext context) { + return TDTextarea( + controller: controller[7], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + layout: TDTextareaLayout.vertical, + bordered: true, + onChanged: (value) { + }, + ); + } + + @Demo(group: 'textarea') + Widget _setWidth(BuildContext context) { + return TDTextarea( + controller: controller[8], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + width: 200, + onChanged: (value) { + }, + ); + } + + @Demo(group: 'textarea') + Widget _setLabel(BuildContext context) { + return TDTextarea( + controller: controller[9], + label: '地址信息', + // labelWidth: 100, + labelIcon: Icon( + TDIcons.location, + size: 20, + color: TDTheme.of(context).fontGyColor1, + ), + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + onChanged: (value) { + }, + ); + } + + @Demo(group: 'textarea') + Widget _setStatus(BuildContext context) { + return TDTextarea( + controller: controller[10], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + layout: TDTextareaLayout.vertical, + required: true, + additionInfo: '辅助说明', + onChanged: (value) { + }, + ); + } + + @Demo(group: 'textarea') + Widget _smallSize(BuildContext context) { + return TDTextarea( + controller: controller[11], + label: '标签文字', + hintText: '请输入文字', + maxLines: 4, + minLines: 4, + maxLength: 500, + indicator: true, + layout: TDTextareaLayout.vertical, + size: TDInputSize.small, + onChanged: (value) { + }, + ); + } +} diff --git a/tdesign-component/example/lib/page/td_theme_page.dart b/tdesign-component/example/lib/page/td_theme_page.dart new file mode 100644 index 000000000..082378005 --- /dev/null +++ b/tdesign-component/example/lib/page/td_theme_page.dart @@ -0,0 +1,337 @@ +import 'package:flutter/material.dart'; + +/// 组件库相关的,只需要引入这个文件,里面暴露td前缀所有需要的类 +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +/// 主题颜色示例页 +class TDThemeColorsPage extends StatefulWidget { + const TDThemeColorsPage({Key? key}) : super(key: key); + + @override + _TDThemeColorsPageState createState() => _TDThemeColorsPageState(); +} + +class _TDThemeColorsPageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '点击标题栏右上角图标可查看使用示例代码', + exampleCodeGroup: 'theme', + children: [ + ExampleModule(title: '颜色示例', children: [ + ExampleItem( + desc: '功能色', builder: _buildFunctionColor, ignoreCode: true), + ExampleItem( + desc: '文字&图标颜色', builder: _buildTextColor, ignoreCode: true), + ExampleItem(desc: '中性色板', builder: _buildOtherColor, ignoreCode: true) + ]) + ], + test: [ + ExampleItem(builder: _buildDefaultTheme), + ExampleItem(builder: _buildCustomTheme) + ], + ); + } + + var brandMap = {}; + var errorMap = {}; + var warningMap = {}; + var successMap = {}; + var fontMap = {}; + var grayMap = {}; + + @override + void initState() { + super.initState(); + _initData(); + } + + void _initData() async{ + + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + TDTheme.of(context).colorMap.forEach((key, value) { + if (key.startsWith('brand')) { + brandMap[key] = value; + } else if (key.startsWith('error')) { + errorMap[key] = value; + } else if (key.startsWith('warning')) { + warningMap[key] = value; + } else if (key.startsWith('success')) { + successMap[key] = value; + } else if (key.startsWith('font')) { + fontMap[key] = value; + } else { + grayMap[key] = value; + } + }); + + TDTheme.of(context).refMap.forEach((key, value) { + var color = TDTheme.of(context).colorMap[key]; + if(color == null){ + return; + } + if (key.startsWith('brand')) { + brandMap[key] = color; + } else if (key.startsWith('error')) { + errorMap[key] = color; + } else if (key.startsWith('warning')) { + warningMap[key] = color; + } else if (key.startsWith('success')) { + successMap[key] = color; + } else if (key.startsWith('font')) { + fontMap[key] = color; + } else { + grayMap[key] = color; + } + }); + setState(() { + + }); + }); + } + + @Demo(group: 'theme') + Widget _buildDefaultTheme(BuildContext context) { + // 通过TDTheme.of(context).xxx使用公共主题属性 + return Container( + margin: EdgeInsets.all(TDTheme.of(context).spacer8), // 间隔 + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, // 颜色 + borderRadius: + BorderRadius.circular(TDTheme.of(context).radiusDefault), // 圆角 + boxShadow: TDTheme.of(context).shadowsBase, // 阴影 + ), + child: TDText( + '使用外层默认主题', + font: TDTheme.of(context).fontBodyLarge, // 字体,业务方使用时, + textColor: + TDTheme.of(context).brandNormalColor, // 颜色,AS中点击颜色可查看具体设置和显示效果 + ), + ); + } + + @Demo(group: 'theme') + Widget _buildCustomTheme(BuildContext context) { + /// 开启多主题 + TDTheme.needMultiTheme(true); + /// 此处替换主题 + return TDTheme( + // 替换fonts和colors,其他主题从父类拷贝 + data: TDTheme.of(context).copyWithTDThemeData('custom', fontMap: { + 'fontBodyLarge': Font(size: 40, lineHeight: 80), + }, colorMap: { + 'brandNormalColor': Colors.red + }), + // 不能直接在此处使用context,这里虽然被包裹在TGTheme中,但是context未更新,因此阿不到最新数据 + child: const TestWidget()); + + // /// 测试控件 +// class TestWidget extends StatelessWidget { +// const TestWidget({Key? key}) : super(key: key); +// +// @override +// Widget build(BuildContext context) { +// return Container( +// alignment: Alignment.center, +// child: Column( +// mainAxisSize: MainAxisSize.min, +// children: [ +// TDText( +// '使用内层赋值主题', +// font: TDTheme.of(context).fontBodyLarge, //明确使用内层主题,必须传context +// textColor: +// TDTheme.of(context).brandNormalColor, // 明确使用内层主题,必须传context +// ), +// TDText( +// '使用内层不赋值主题', +// font: TDTheme.of(context).fontTitleExtraLarge, //明确使用内层主题,必须传context +// textColor: +// TDTheme.of(context).successNormalColor, // 明确使用内层主题,必须传context +// ), +// TDText( +// '使用默认主题', +// font: TDTheme.defaultData().fontBodyLarge, //不传context,使用默认主题,此处是外层的主题 +// textColor: TDTheme.defaultData().brandNormalColor, +// ), +// ], +// ), +// ); +// } +// } + } + + Widget _buildFunctionColor(BuildContext context) { + var spList = ['Light', 'Focus', 'Disabled', 'Hover', 'Normal', 'Click']; + var functionList = [ + 'brand', + 'error', + 'warning', + 'success', + ]; + if (brandMap.length == errorMap.length && + warningMap.length == successMap.length && + brandMap.length == warningMap.length) { + return ListView.builder( + physics: const NeverScrollableScrollPhysics(), + itemCount: brandMap.length * 4, + shrinkWrap: true, + padding: const EdgeInsets.all(16), + itemBuilder: (context, index) { + var type = index ~/ brandMap.length; + index = index % brandMap.length; + var function = functionList[type]; + var map = {}; + if(type == 0){ + map = brandMap; + } else if(type == 1){ + map = errorMap; + } else if (type == 2){ + map = warningMap; + } else if(type == 3){ + map = successMap; + } + if (index < 10) { + return Container( + color: TDTheme.of(context) + .colorMap['${function}Color${index + 1}'], + child: TDText('${function}Color${index + 1}'), + ); + } else { + return Container( + color: map.values.elementAt(index), + child: TDText(map.keys.elementAt(index)), + ); + } + }); + } else { + return TDText( + '功能色数量不一样', + textColor: TDTheme.of(context).errorNormalColor, + ); + } + } + + Widget _buildTextColor(BuildContext context) { + var textList = ['Gy', 'Wh']; + return ListView.builder( + physics: const NeverScrollableScrollPhysics(), + itemCount: fontMap.length, + shrinkWrap: true, + itemBuilder: (context, index) { + var light = (index - 3).abs() < 2; + var type = index ~/ 4; + index = index % 4; + var function = textList[type]; + return Container( + padding: const EdgeInsets.only(left: 16, right: 16), + color: type == 0 ? Colors.white : Colors.black, + child: Container( + color: TDTheme.of(context) + .colorMap['font${function}Color${index + 1}'], + child: TDText( + 'font${function}Color${index + 1}', + textColor: light ? Colors.black : Colors.white, + ), + ), + ); + }); + } + + Widget _buildOtherColor(BuildContext context) { + return ListView.builder( + physics: const NeverScrollableScrollPhysics(), + itemCount: grayMap.length, + padding: const EdgeInsets.all(16), + shrinkWrap: true, + itemBuilder: (context, index) { + var light = index < 6; + if (index == 0) { + return Container( + color: TDTheme.of(context).colorMap['whiteColor1'], + child: const TDText('whiteColor1'), + ); + } else { + return Container( + color: TDTheme.of(context).colorMap['grayColor${index}'], + child: TDText( + 'grayColor${index}', + textColor: light ? Colors.black : Colors.white, + ), + ); + } + }); + } +} + +/// 测试控件 +class TestWidget extends StatelessWidget { + const TestWidget({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + alignment: Alignment.center, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TDText( + '使用内层赋值主题', + font: TDTheme.of(context).fontBodyLarge, //明确使用内层主题,必须传context + textColor: + TDTheme.of(context).brandNormalColor, // 明确使用内层主题,必须传context + ), + TDText( + '使用内层不赋值主题', + font: TDTheme.of(context).fontTitleExtraLarge, //明确使用内层主题,必须传context + textColor: + TDTheme.of(context).successNormalColor, // 明确使用内层主题,必须传context + ), + const TDButton( + text: '使用内层赋值主题', + theme: TDButtonTheme.primary, + ), + TDText( + '使用默认主题', + font: + TDTheme.defaultData().fontBodyLarge, //不传context,使用默认主题,此处是外层的主题 + textColor: TDTheme.defaultData().brandNormalColor, + ), + ], + ), + ); + } +} + +/// 扩展主题属性示例 +extension TGLayouts on TDThemeData { + /// 因为扩展中不能声明字段,只能借助TDExtraThemeData + double get layout1 => ofExtra()?.layouts['layout1'] ?? 0; + + Data2? get data2 => ofExtra()?.data2; +} + +class LayoutExtra extends TDExtraThemeData { + Map layouts = {}; + Data2? data2; + + @override + void parse(String name, Map curThemeMap) { + // TODO: implement parse + } +} + +/// 二级扩展测试 +class Data2 {} + +extension Data2Ext on Data2 { + String get test => 'test'; +} + +void test() { + TDTheme.of(null).layout1; + TDTheme.of(null).data2!.test; +} diff --git a/tdesign-component/example/lib/page/td_time_counter_page.dart b/tdesign-component/example/lib/page/td_time_counter_page.dart new file mode 100644 index 000000000..a1272d54a --- /dev/null +++ b/tdesign-component/example/lib/page/td_time_counter_page.dart @@ -0,0 +1,560 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDTimeCounterPage extends StatelessWidget { + const TDTimeCounterPage({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + color: TDTheme.of(context).grayColor2, + child: ExamplePage( + title: tdTitle(context), + desc: '用于实时展示计时数值。', + exampleCodeGroup: 'timeCounter', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem( + ignoreCode: true, + desc: '时分秒', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildSimple); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '带毫秒', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildMillisecondSimple); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '正向计时', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildUpSimple); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '带方形底', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildSquareSimple); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '带圆形底', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildRoundSimple); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '带单位', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildUnitSimple); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '无底色带单位', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildCustomUnitSimple); + }, + ), + ]), + ExampleModule(title: '组件尺寸', children: [ + ExampleItem( + ignoreCode: true, + desc: '纯数字', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return Container( + alignment: Alignment.topLeft, + child: Wrap(spacing: 8, direction: Axis.vertical, children: [ + Row( + children: const [ + SizedBox( + width: 80, + child: Text('小'), + ), + CodeWrapper(builder: _buildSmallSize), + ], + ), + Row( + children: const [ + SizedBox( + width: 80, + child: Text('中'), + ), + CodeWrapper(builder: _buildMediumSize), + ], + ), + Row( + children: const [ + SizedBox( + width: 80, + child: Text('大'), + ), + CodeWrapper(builder: _buildLargeSize), + ], + ), + ]), + ); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '带方形底', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return Container( + alignment: Alignment.topLeft, + child: Wrap(spacing: 8, direction: Axis.vertical, children: [ + Row( + children: const [ + SizedBox( + width: 80, + child: Text('小'), + ), + CodeWrapper(builder: _buildSquareSmallSize), + ], + ), + Row( + children: const [ + SizedBox( + width: 80, + child: Text('中'), + ), + CodeWrapper(builder: _buildSquareMediumSize), + ], + ), + Row( + children: const [ + SizedBox( + width: 80, + child: Text('大'), + ), + CodeWrapper(builder: _buildSquareLargeSize), + ], + ), + ]), + ); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '带圆形底', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return Container( + alignment: Alignment.topLeft, + child: Wrap(spacing: 8, direction: Axis.vertical, children: [ + Row( + children: const [ + SizedBox( + width: 80, + child: Text('小'), + ), + CodeWrapper(builder: _buildRoundSmallSize), + ], + ), + Row( + children: const [ + SizedBox( + width: 80, + child: Text('中'), + ), + CodeWrapper(builder: _buildRoundMediumSize), + ], + ), + Row( + children: const [ + SizedBox( + width: 80, + child: Text('大'), + ), + CodeWrapper(builder: _buildRoundLargeSize), + ], + ), + ]), + ); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '带单位', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return Container( + alignment: Alignment.topLeft, + child: Wrap(spacing: 8, direction: Axis.vertical, children: [ + Row( + children: const [ + SizedBox( + width: 80, + child: Text('小'), + ), + CodeWrapper(builder: _buildUnitSmallSize), + ], + ), + Row( + children: const [ + SizedBox( + width: 80, + child: Text('中'), + ), + CodeWrapper(builder: _buildUnitMediumSize), + ], + ), + Row( + children: const [ + SizedBox( + width: 80, + child: Text('大'), + ), + CodeWrapper(builder: _buildUnitLargeSize), + ], + ), + ]), + ); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '无底色带单位', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return Container( + alignment: Alignment.topLeft, + child: Wrap(spacing: 8, direction: Axis.vertical, children: [ + Row( + children: const [ + SizedBox( + width: 80, + child: Text('小'), + ), + CodeWrapper(builder: _buildCustomUnitSmallSize), + ], + ), + Row( + children: const [ + SizedBox( + width: 80, + child: Text('中'), + ), + CodeWrapper(builder: _buildCustomUnitMediumSize), + ], + ), + Row( + children: const [ + SizedBox( + width: 80, + child: Text('大'), + ), + CodeWrapper(builder: _buildCustomUnitLargeSize), + ], + ), + ]), + ); + }, + ), + ]), + ], + test: [ + ExampleItem( + ignoreCode: true, + desc: '控制倒计时', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildControl); + }, + ), + ExampleItem( + ignoreCode: true, + desc: '自定义显示位数', + center: false, + padding: const EdgeInsets.only(left: 16), + builder: (BuildContext context) { + return const CodeWrapper(builder: _buildCustomNum); + }, + ), + ], + ), + ); + } +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildSimple(BuildContext context) { + return const TDTimeCounter(time: 60 * 60 * 1000); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildMillisecondSimple(BuildContext context) { + return const TDTimeCounter(time: 60 * 60 * 1000, millisecond: true); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildUpSimple(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + millisecond: true, + direction: TDTimeCounterDirection.up, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildSquareSimple(BuildContext context) { + return const TDTimeCounter(time: 60 * 60 * 1000, theme: TDTimeCounterTheme.square); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildRoundSimple(BuildContext context) { + return const TDTimeCounter(time: 60 * 60 * 1000, theme: TDTimeCounterTheme.round); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildUnitSimple(BuildContext context) { + return const TDTimeCounter(time: 60 * 60 * 1000, theme: TDTimeCounterTheme.square, splitWithUnit: true); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildCustomUnitSimple(BuildContext context) { + var style = TDTimeCounterStyle.generateStyle(context); + style.timeColor = TDTheme.of(context).errorColor6; + return TDTimeCounter(time: 60 * 60 * 1000, splitWithUnit: true, style: style); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildSmallSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.small, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildMediumSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.medium, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildLargeSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.large, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildSquareSmallSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.small, + theme: TDTimeCounterTheme.square, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildSquareMediumSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.medium, + theme: TDTimeCounterTheme.square, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildSquareLargeSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.large, + theme: TDTimeCounterTheme.square, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildRoundSmallSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.small, + theme: TDTimeCounterTheme.round, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildRoundMediumSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.medium, + theme: TDTimeCounterTheme.round, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildRoundLargeSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.large, + theme: TDTimeCounterTheme.round, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildUnitSmallSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.small, + theme: TDTimeCounterTheme.square, + splitWithUnit: true, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildUnitMediumSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.medium, + theme: TDTimeCounterTheme.square, + splitWithUnit: true, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildUnitLargeSize(BuildContext context) { + return const TDTimeCounter( + time: 60 * 60 * 1000, + size: TDTimeCounterSize.large, + theme: TDTimeCounterTheme.square, + splitWithUnit: true, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildCustomUnitSmallSize(BuildContext context) { + var style = TDTimeCounterStyle.generateStyle(context, size: TDTimeCounterSize.small); + style.timeColor = TDTheme.of(context).errorColor6; + return TDTimeCounter( + time: 60 * 60 * 1000, + splitWithUnit: true, + style: style, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildCustomUnitMediumSize(BuildContext context) { + var style = TDTimeCounterStyle.generateStyle(context, size: TDTimeCounterSize.medium); + style.timeColor = TDTheme.of(context).errorColor6; + return TDTimeCounter( + time: 60 * 60 * 1000, + splitWithUnit: true, + style: style, + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildCustomUnitLargeSize(BuildContext context) { + var style = TDTimeCounterStyle.generateStyle(context, size: TDTimeCounterSize.large); + style.timeColor = TDTheme.of(context).errorColor6; + return TDTimeCounter( + time: 60 * 60 * 1000, + splitWithUnit: true, + style: style, + ); +} + +@Demo(group: 'timeCounter') +Widget _buildControl(BuildContext context) { + var controller = TDTimeCounterController(); + return Wrap( + direction: Axis.vertical, + spacing: 8, + children: [ + Wrap( + spacing: 8, + children: [ + TDButton( + text: '开始', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.start(); + }, + ), + TDButton( + text: '结束', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.reset(0); + }, + ), + TDButton( + text: '重置', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.reset(); + }, + ), + TDButton( + text: '暂停', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.pause(); + }, + ), + TDButton( + text: '继续', + size: TDButtonSize.extraSmall, + theme: TDButtonTheme.primary, + onTap: () { + controller.resume(); + }, + ), + ], + ), + TDTimeCounter( + time: 60 * 60 * 1000, + controller: controller, + // autoStart: false, + ), + ], + ); +} + +@Demo(group: 'timeCounter') +TDTimeCounter _buildCustomNum(BuildContext context) { + return const TDTimeCounter( + time: 2000 * 60 * 1000, + format: 'mmmmmmm分sss秒', + ); +} diff --git a/tdesign-component/example/lib/page/td_toast_page.dart b/tdesign-component/example/lib/page/td_toast_page.dart new file mode 100644 index 000000000..2f356f031 --- /dev/null +++ b/tdesign-component/example/lib/page/td_toast_page.dart @@ -0,0 +1,302 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/src/util/auto_size.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../../base/example_widget.dart'; +import '../annotation/demo.dart'; + +class TDToastPage extends StatefulWidget { + const TDToastPage({Key? key}) : super(key: key); + + @override + State createState() => _TDToastPageState(); +} + +class _TDToastPageState extends State { + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '用于轻量级反馈或提示,不会打断用户操作。', + exampleCodeGroup: 'toast', + children: [ + ExampleModule(title: '组件类型', children: [ + ExampleItem(desc: '纯文字', builder: _textToast), + ExampleItem(desc: '多行文字', builder: _multipleToast), + ExampleItem(desc: '带横向图标', builder: _horizontalIconToast), + ExampleItem(desc: '带竖向图标', builder: _verticalIconToast), + ExampleItem(desc: '加载状态', builder: _loadingToast), + ExampleItem(desc: '加载状态自定义', builder: _loadingCustomToast), + ExampleItem(desc: '加载状态(无文字)', builder: _loadingWithoutTextToast), + ExampleItem(desc: '停止加载', builder: _dismissLoadingToast), + ExampleItem(desc: '自定义纯文字', builder: _textCustomToast), + ]), + ExampleModule(title: '组件状态', children: [ + ExampleItem(desc: '成功提示', builder: _successToast), + ExampleItem(desc: '成功提示(竖向)', builder: _successVerticalToast), + ExampleItem(desc: '警告提示', builder: _warningToast), + ExampleItem(desc: '警告提示(竖向)', builder: _warningVerticalToast), + ExampleItem(desc: '失败提示', builder: _failToast), + ExampleItem(desc: '失败提示(竖向)', builder: _failVerticalToast), + ]) + ], + test: [ + ExampleItem(desc: '禁止滚动+点击', builder: _preventTapToast), + ExampleItem(desc: '自定义宽度+行数', builder: _customMultipleToast), + ], + ); + } + + @Demo(group: 'toast') + Widget _textToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showText('轻提示文字内容', context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '纯文字', + ); + } + + @Demo(group: 'toast') + Widget _textCustomToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showText('自定义纯文字', + context: context, + customWidget: Container( + width: 50, + height: 20, + child: const TDText('自定义纯文字'), + color: TDTheme.of(context).brandClickColor, + )); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '纯文字', + ); + } + + @Demo(group: 'toast') + Widget _multipleToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showText('最多一行展示十个汉字宽度限制最多不超过三行文字', context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '多行文字', + ); + } + + @Demo(group: 'toast') + Widget _horizontalIconToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showIconText('带横向图标', icon: TDIcons.check_circle, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '带横向图标', + ); + } + + @Demo(group: 'toast') + Widget _verticalIconToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showIconText('带竖向图标', + icon: TDIcons.check_circle, direction: IconTextDirection.vertical, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '带竖向图标', + ); + } + + @Demo(group: 'toast') + Widget _loadingToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showLoading(context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '加载状态', + ); + } + + @Demo(group: 'toast') + Widget _loadingCustomToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showLoading(context: context, customWidget: Container( + width: 50, + height: 20, + child: const TDText('自定义加载'), + color: TDTheme.of(context).brandColor1, + )); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '加载状态', + ); + } + + @Demo(group: 'toast') + Widget _loadingWithoutTextToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showLoadingWithoutText(context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '加载状态(无文案)', + ); + } + + @Demo(group: 'toast') + Widget _dismissLoadingToast(BuildContext context) { + return const TDButton( + onTap: TDToast.dismissLoading, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '停止加载', + ); + } + + @Demo(group: 'toast') + Widget _successToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showSuccess('成功文案', context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '成功提示', + ); + } + + @Demo(group: 'toast') + Widget _successVerticalToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showSuccess('成功文案', direction: IconTextDirection.vertical, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '成功提示(竖向)', + ); + } + + @Demo(group: 'toast') + Widget _warningToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showWarning('警告文案', direction: IconTextDirection.horizontal, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '警告提示', + ); + } + + @Demo(group: 'toast') + Widget _warningVerticalToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showWarning('警告文案', direction: IconTextDirection.vertical, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '警告提示(竖向)', + ); + } + + @Demo(group: 'toast') + Widget _failToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showFail('失败文案', direction: IconTextDirection.horizontal, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '失败提示', + ); + } + + @Demo(group: 'toast') + Widget _failVerticalToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showFail('失败文案', direction: IconTextDirection.vertical, context: context); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '失败提示(竖向)', + ); + } + + @Demo(group: 'toast') + Widget _preventTapToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showText('轻提示文字内容', context: context, preventTap: true, backgroundColor: Colors.black.withOpacity(0.7)); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '禁止滚动+点击', + ); + } + + @Demo(group: 'toast') + Widget _customMultipleToast(BuildContext context) { + return TDButton( + onTap: () { + TDToast.showText( + '最多一行展示十个汉字宽度限制最多不超过三行文字最多一行展示十个汉字宽度限制最多不超过三行文字最多一行展示十个汉字宽度限制最多不超过三行文字最多一行展示十个汉字宽度限制最多不超过三行文字最多一行展示十个汉字宽度限制最多不超过三行文字最多一行展示十个汉字宽度限制最多不超过三行文字', + context: context, + constraints: BoxConstraints(maxWidth: 350.scale), + maxLines: 5); + }, + size: TDButtonSize.large, + type: TDButtonType.outline, + theme: TDButtonTheme.primary, + isBlock: true, + text: '多行文字', + ); + } +} diff --git a/tdesign-component/example/lib/page/td_tree_select_page.dart b/tdesign-component/example/lib/page/td_tree_select_page.dart new file mode 100644 index 000000000..3653f1509 --- /dev/null +++ b/tdesign-component/example/lib/page/td_tree_select_page.dart @@ -0,0 +1,154 @@ +import 'package:flutter/cupertino.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDTreeSelectPage extends StatefulWidget { + const TDTreeSelectPage({Key? key}) : super(key: key); + + @override + State createState() => _TDTreeSelectPageState(); +} + +class _TDTreeSelectPageState extends State { + String? inputText; + List values1 = [ + 1, + 11, + ]; + List values2 = [ + 1, + [11, 12, 13], + ]; + List values3 = [1, 11, 111]; + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + desc: '适用于选择树形的数据结构', + exampleCodeGroup: 'tree', + backgroundColor: TDTheme.of(context).grayColor2, + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '基础树形选择', builder: _buildDefaultTreeSelect), + ExampleItem(desc: '多选树形选择', builder: _buildMultipleTreeSelect), + ], + ), + ExampleModule( + title: '组件状态', + children: [ + ExampleItem(desc: '三级树形选择', builder: _buildThirdTreeSelect), + ], + ), + ], + test: [ + ExampleItem(desc: '局部多选', builder: _buildPartMultipleTreeSelect), + ], + ); + } + + @Demo(group: 'tree') + Widget _buildDefaultTreeSelect(BuildContext context) { + var options = []; + + for (var i = 1; i <= 10; i++) { + options.add(TDSelectOption(label: '选项$i', value: i, children: [])); + + for (var j = 1; j <= 10; j++) { + options[i - 1].children.add(TDSelectOption( + label: '选项$i.$j', + value: i * 10 + j, + children: [], + )); + } + } + + return TDTreeSelect( + options: options, + defaultValue: values1, + onChange: (val, level) { + print('$val, $level'); + }, + ); + } + + @Demo(group: 'tree') + Widget _buildMultipleTreeSelect(BuildContext context) { + var options = []; + + for (var i = 1; i <= 10; i++) { + options.add(TDSelectOption(label: '选项$i', value: i, children: [])); + + for (var j = 1; j <= 10; j++) { + options[i - 1].children.add( + TDSelectOption(label: '选项$i.$j', value: i * 10 + j, children: [])); + } + } + + return TDTreeSelect( + options: options, + defaultValue: values2, + multiple: true, + onChange: (val, level) { + print('$val, $level'); + }, + ); + } + + @Demo(group: 'tree') + Widget _buildThirdTreeSelect(BuildContext context) { + var options = []; + + for (var i = 1; i <= 10; i++) { + options.add(TDSelectOption(label: '选项$i', value: i, children: [])); + + for (var j = 1; j <= 10; j++) { + options[i - 1].children.add( + TDSelectOption(label: '选项$i.$j', value: i * 10 + j, children: [])); + + for (var k = 1; k <= 10; k++) { + options[i - 1].children[j - 1].children.add( + TDSelectOption(label: '选项$i.$j.$k', value: i * 100 + j * 10 + k)); + } + } + } + + return TDTreeSelect( + options: options, + defaultValue: values3, + onChange: (val, level) { + print('$val, $level'); + }, + ); + } + + @Demo(group: 'tree') + Widget _buildPartMultipleTreeSelect(BuildContext context) { + var options = []; + + for (var i = 1; i <= 2; i++) { + options.add(TDSelectOption( + label: '${i == 1 ? '单选' : '多选'}', value: i, children: [])); + + for (var j = 1; j <= 10; j++) { + options[i - 1].children.add(TDSelectOption( + label: '选项$i.$j', + value: i * 10 + j, + children: [], + multiple: i == 2)); + } + } + + return TDTreeSelect( + options: options, + defaultValue: values1, + onChange: (val, level) { + print('$val, $level'); + }, + ); + } +} diff --git a/tdesign-component/example/lib/page/td_upload_page.dart b/tdesign-component/example/lib/page/td_upload_page.dart new file mode 100644 index 000000000..83bfa0b03 --- /dev/null +++ b/tdesign-component/example/lib/page/td_upload_page.dart @@ -0,0 +1,235 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../annotation/demo.dart'; +import '../base/example_widget.dart'; + +class TDUploadPage extends StatefulWidget { + const TDUploadPage({Key? key}) : super(key: key); + + @override + State createState() => TDUploadState(); +} + +class TDUploadState extends State { + final List files1 = []; + final List files2 = [ + TDUploadFile(key: 1, remotePath: 'https://tdesign.gtimg.com/demo/images/example1.png'), + TDUploadFile(key: 2, remotePath: 'https://tdesign.gtimg.com/demo/images/example2.png'), + TDUploadFile(key: 3, remotePath: 'https://tdesign.gtimg.com/demo/images/example3.png'), + ]; + final List files3 = [ + TDUploadFile( + key: 1, + status: TDUploadFileStatus.loading, + loadingText: '上传中...', + remotePath: 'https://tdesign.gtimg.com/demo/images/example1.png'), + TDUploadFile( + key: 2, + status: TDUploadFileStatus.loading, + progress: 68, + remotePath: 'https://tdesign.gtimg.com/demo/images/example1.png'), + ]; + final List files4 = [ + TDUploadFile( + key: 1, + status: TDUploadFileStatus.retry, + retryText: '重新上传', + remotePath: 'https://tdesign.gtimg.com/demo/images/example1.png'), + ]; + final List files5 = [ + TDUploadFile( + key: 1, + status: TDUploadFileStatus.error, + errorText: '上传失败', + remotePath: 'https://tdesign.gtimg.com/demo/images/example4.png'), + ]; + final List files6 = []; + + void onValueChanged(List fileList, List value, TDUploadType event) { + switch (event) { + case TDUploadType.add: + setState(() { + fileList.addAll(value); + }); + break; + case TDUploadType.remove: + setState(() { + fileList.removeWhere((element) => element.key == value[0].key); + }); + break; + case TDUploadType.replace: + setState(() { + final firstReplaceFile = value.first; + final index = fileList.indexWhere((file) => file.key == firstReplaceFile.key); + if (index != -1) { + fileList[index] = firstReplaceFile; + } + }); + break; + } + } + + void onClick(int key) { + print('点击 $key'); + } + + void onCancel() { + print('取消'); + } + + @override + Widget build(BuildContext context) { + return ExamplePage( + title: tdTitle(), + exampleCodeGroup: 'upload', + desc: '用于相册读取或拉起拍照的图片上传功能。${PlatformUtil.isWeb ? "Web端不支持读取本地图片,请前往移动端体验。" : ""}', + children: [ + ExampleModule( + title: '组件类型', + children: [ + ExampleItem(desc: '单选上传', builder: _uploadSingle), + ExampleItem(desc: '单选上传(替换)', builder: _uploadSingleWithReplace), + ExampleItem(desc: '多选上传', builder: _uploadMultiple), + ], + ), + ExampleModule( + title: '组件状态', + children: [ + ExampleItem(desc: '加载状态', builder: _uploadLoading), + ExampleItem(desc: '重新上传', builder: _uploadRetry), + ExampleItem(desc: '上传失败', builder: _uploadError), + ], + ), + ], + test: [ + ExampleItem(ignoreCode: true, desc: '单选快速替换, 大小和图形测试', builder: _uploadSingleWithReplace), + ExampleItem(ignoreCode: true, desc: '上传文件大小限制,10KB', builder: _uploadSizeLimit), + ], + ); + } + + Widget wrapDemoContainer(String title, {required Widget child}) { + return Container( + padding: const EdgeInsets.all(16), + decoration: const BoxDecoration(color: Colors.white), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TDText( + title, + style: const TextStyle(fontSize: 16), + ), + const SizedBox( + height: 16, + ), + child + ], + ), + ); + } + + @Demo(group: 'upload') + Widget _uploadSingle(BuildContext context) { + return wrapDemoContainer('单选上传', + child: TDUpload( + files: files1, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files1, files, type)), + )); + } + + @Demo(group: 'upload') + Widget _uploadSingleWithReplace(BuildContext context) { + return wrapDemoContainer('单选上传(替换)', + child: TDUpload( + files: files6, + width: 60, + height: 60, + type: TDUploadBoxType.circle, + enabledReplaceType: true, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files6, files, type)), + )); + } + + @Demo(group: 'upload') + Widget _uploadMultiple(BuildContext context) { + return wrapDemoContainer('多选上传', + child: TDUpload( + files: files2, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files2, files, type)), + )); + } + + @Demo(group: 'upload') + Widget _uploadLoading(BuildContext context) { + return wrapDemoContainer('上传图片', + child: TDUpload( + files: files3, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files3, files, type)), + )); + } + + @Demo(group: 'upload') + Widget _uploadRetry(BuildContext context) { + return wrapDemoContainer('上传图片', + child: TDUpload( + files: files4, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files4, files, type)), + )); + } + + @Demo(group: 'upload') + Widget _uploadError(BuildContext context) { + return wrapDemoContainer('上传图片', + child: TDUpload( + files: files5, + multiple: true, + max: 9, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + onChange: ((files, type) => onValueChanged(files5, files, type)), + )); + } + + @Demo(group: 'upload') + Widget _uploadSizeLimit(BuildContext context) { + return wrapDemoContainer('限制10KB', + child: TDUpload( + files: files1, + onClick: onClick, + onCancel: onCancel, + onError: print, + onValidate: print, + sizeLimit: 10, + onChange: ((files, type) => onValueChanged(files1, files, type)), + )); + } +} diff --git a/tdesign-component/example/lib/page/todo_page.dart b/tdesign-component/example/lib/page/todo_page.dart new file mode 100644 index 000000000..3adf9b81f --- /dev/null +++ b/tdesign-component/example/lib/page/todo_page.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; + +import '../base/example_widget.dart'; + +class TodoPage extends StatelessWidget { + const TodoPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: ScrollbarTheme( + data: ScrollbarThemeData( + trackVisibility: MaterialStateProperty.all(true)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const TDNavBar( + title: 'TODO', + ), + Expanded( + child: Container( + alignment: Alignment.center, + margin: const EdgeInsets.all(16), + child: const TDText('欢迎使用TDesign,该组件已在规划中,请关注TDesign项目最新动态'), + ), + ) + ], + ))); + } +} diff --git a/tdesign-component/example/ohos/.gitignore b/tdesign-component/example/ohos/.gitignore new file mode 100644 index 000000000..6ca13b317 --- /dev/null +++ b/tdesign-component/example/ohos/.gitignore @@ -0,0 +1,19 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +*.har +**/BuildProfile.ets +**/oh-package-lock.json5 + +**/src/main/resources/rawfile/flutter_assets/ +**/libs/arm64-v8a/libapp.so +**/libs/arm64-v8a/libflutter.so +**/libs/arm64-v8a/libvmservice_snapshot.so diff --git a/tdesign-component/example/ohos/AppScope/app.json5 b/tdesign-component/example/ohos/AppScope/app.json5 new file mode 100644 index 000000000..388895013 --- /dev/null +++ b/tdesign-component/example/ohos/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.tdesign.tdesign_flutter_example", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/tdesign-component/example/ohos/AppScope/resources/base/element/string.json b/tdesign-component/example/ohos/AppScope/resources/base/element/string.json new file mode 100644 index 000000000..0c14942e1 --- /dev/null +++ b/tdesign-component/example/ohos/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "tdesign_flutter_example" + } + ] +} diff --git a/tdesign-component/example/ohos/AppScope/resources/base/media/app_icon.png b/tdesign-component/example/ohos/AppScope/resources/base/media/app_icon.png new file mode 100644 index 000000000..ce307a882 Binary files /dev/null and b/tdesign-component/example/ohos/AppScope/resources/base/media/app_icon.png differ diff --git a/tdesign-component/example/ohos/build-profile.json5 b/tdesign-component/example/ohos/build-profile.json5 new file mode 100644 index 000000000..ac945abcd --- /dev/null +++ b/tdesign-component/example/ohos/build-profile.json5 @@ -0,0 +1,56 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "app": { + "signingConfigs": [ + { + "name": "default", + "type": "HarmonyOS", + "material": { + "certpath": "/Users/kavinhuang/.ohos/config/default_ohos_E3ti-Qv7lm3aSRYR2xnvEB-12Uni-e3qIbn3ajkUok8=.cer", + "storePassword": "0000001ABB2898AAF97D6D2388883E9B35446DAEDAC4FA6ADADCF1209E9CD83E7F39DF87C695A38EECCE", + "keyAlias": "debugKey", + "keyPassword": "0000001A004161F1E5A4764605A986522FE39D40DE580859BC3B8179D5113BC4B8F4D3CE6F8AB55B4D3A", + "profile": "/Users/kavinhuang/.ohos/config/default_ohos_E3ti-Qv7lm3aSRYR2xnvEB-12Uni-e3qIbn3ajkUok8=.p7b", + "signAlg": "SHA256withECDSA", + "storeFile": "/Users/kavinhuang/.ohos/config/default_ohos_E3ti-Qv7lm3aSRYR2xnvEB-12Uni-e3qIbn3ajkUok8=.p12" + } + } + ], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS", + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/.gitignore b/tdesign-component/example/ohos/entry/.gitignore new file mode 100644 index 000000000..2795a1c5b --- /dev/null +++ b/tdesign-component/example/ohos/entry/.gitignore @@ -0,0 +1,7 @@ + +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/build-profile.json5 b/tdesign-component/example/ohos/entry/build-profile.json5 new file mode 100644 index 000000000..633d360fb --- /dev/null +++ b/tdesign-component/example/ohos/entry/build-profile.json5 @@ -0,0 +1,29 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default", + "runtimeOS": "HarmonyOS" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/hvigorfile.ts b/tdesign-component/example/ohos/entry/hvigorfile.ts new file mode 100644 index 000000000..894fc15c6 --- /dev/null +++ b/tdesign-component/example/ohos/entry/hvigorfile.ts @@ -0,0 +1,17 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { hapTasks } from '@ohos/hvigor-ohos-plugin'; diff --git a/tdesign-component/example/ohos/entry/oh-package.json5 b/tdesign-component/example/ohos/entry/oh-package.json5 new file mode 100644 index 000000000..803b25293 --- /dev/null +++ b/tdesign-component/example/ohos/entry/oh-package.json5 @@ -0,0 +1,9 @@ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets b/tdesign-component/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 000000000..8bc48be87 --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,24 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos'; +import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant'; + +export default class EntryAbility extends FlutterAbility { + configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + GeneratedPluginRegistrant.registerWith(flutterEngine) + } +} diff --git a/tdesign-component/example/ohos/entry/src/main/ets/pages/Index.ets b/tdesign-component/example/ohos/entry/src/main/ets/pages/Index.ets new file mode 100644 index 000000000..1125f9fdd --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,38 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import common from '@ohos.app.ability.common'; +import { FlutterPage } from '@ohos/flutter_ohos' + +let storage = LocalStorage.getShared() +const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS' + +@Entry(storage) +@Component +struct Index { + private context = getContext(this) as common.UIAbilityContext + @LocalStorageLink('viewId') viewId: string = ""; + + build() { + Column() { + FlutterPage({ viewId: this.viewId }) + } + } + + onBackPress(): boolean { + this.context.eventHub.emit(EVENT_BACK_PRESS) + return true + } +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets b/tdesign-component/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets new file mode 100644 index 000000000..f28ced70d --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets @@ -0,0 +1,24 @@ +import { FlutterEngine, Log } from '@ohos/flutter_ohos'; + +/** + * Generated file. Do not edit. + * This file is generated by the Flutter tool based on the + * plugins that support the Ohos platform. + */ + +const TAG = "GeneratedPluginRegistrant"; + +export class GeneratedPluginRegistrant { + + static registerWith(flutterEngine: FlutterEngine) { + try { + } catch (e) { + Log.e( + TAG, + "Tried to register plugins with FlutterEngine (" + + flutterEngine + + ") failed."); + Log.e(TAG, "Received exception while registering", e); + } + } +} diff --git a/tdesign-component/example/ohos/entry/src/main/module.json5 b/tdesign-component/example/ohos/entry/src/main/module.json5 new file mode 100644 index 000000000..7bbf78b18 --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/main/module.json5 @@ -0,0 +1,53 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "requestPermissions": [ + {"name" : "ohos.permission.INTERNET"}, + ] + } +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/main/resources/base/element/color.json b/tdesign-component/example/ohos/entry/src/main/resources/base/element/color.json new file mode 100644 index 000000000..3c712962d --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/main/resources/base/element/string.json b/tdesign-component/example/ohos/entry/src/main/resources/base/element/string.json new file mode 100644 index 000000000..fc4b945e4 --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "tdesign_flutter_example" + } + ] +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/main/resources/base/media/icon.png b/tdesign-component/example/ohos/entry/src/main/resources/base/media/icon.png new file mode 100644 index 000000000..ce307a882 Binary files /dev/null and b/tdesign-component/example/ohos/entry/src/main/resources/base/media/icon.png differ diff --git a/tdesign-component/example/ohos/entry/src/main/resources/base/profile/main_pages.json b/tdesign-component/example/ohos/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 000000000..1898d94f5 --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/tdesign-component/example/ohos/entry/src/main/resources/en_US/element/string.json b/tdesign-component/example/ohos/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 000000000..fc4b945e4 --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "tdesign_flutter_example" + } + ] +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/main/resources/zh_CN/element/string.json b/tdesign-component/example/ohos/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 000000000..a2856968e --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "tdesign_flutter_example" + } + ] +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets b/tdesign-component/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 000000000..25d4c71ff --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' + +export default function abilityTest() { + describe('ActsAbilityTest', function () { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(function () { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(function () { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(function () { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(function () { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain',0, function () { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc' + let b = 'b' + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b) + expect(a).assertEqual(a) + }) + }) +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/ohosTest/ets/test/List.test.ets b/tdesign-component/example/ohos/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 000000000..f4140030e --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import abilityTest from './Ability.test' + +export default function testsuite() { + abilityTest() +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets b/tdesign-component/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets new file mode 100644 index 000000000..4ca645e60 --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets @@ -0,0 +1,63 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import UIAbility from '@ohos.app.ability.UIAbility'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import hilog from '@ohos.hilog'; +import { Hypium } from '@ohos/hypium'; +import testsuite from '../test/List.test'; +import window from '@ohos.window'; + +export default class TestAbility extends UIAbility { + onCreate(want, launchParam) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate'); + hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? ''); + hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? ''); + var abilityDelegator: any + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var abilityDelegatorArguments: any + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!'); + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) + } + + onDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate'); + windowStage.loadContent('testability/pages/Index', (err, data) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', + JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy'); + } + + onForeground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground'); + } + + onBackground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground'); + } +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets b/tdesign-component/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 000000000..cef0447cd --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; + +@Entry +@Component +struct Index { + aboutToAppear() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear'); + } + @State message: string = 'Hello World' + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + Button() { + Text('next page') + .fontSize(20) + .fontWeight(FontWeight.Bold) + }.type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('35%') + .height('5%') + .onClick(()=>{ + }) + } + .width('100%') + } + .height('100%') + } + } \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/tdesign-component/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 000000000..1def08f2e --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,64 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import hilog from '@ohos.hilog'; +import TestRunner from '@ohos.application.testRunner'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; + +var abilityDelegator = undefined +var abilityDelegatorArguments = undefined + +async function onAbilityCreateCallback() { + hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); +} + +async function addAbilityMonitorCallback(err: any) { + hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); +} + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); + } + + async onRun() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' + let lMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName + var debug = abilityDelegatorArguments.parameters['-D'] + if (debug == 'true') + { + cmd += ' -D' + } + hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd); + abilityDelegator.executeShellCommand(cmd, + (err: any, d: any) => { + hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? ''); + }) + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/ohosTest/module.json5 b/tdesign-component/example/ohos/entry/src/ohosTest/module.json5 new file mode 100644 index 000000000..fab77ce2e --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/ohosTest/module.json5 @@ -0,0 +1,51 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "description": "$string:module_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:test_pages", + "abilities": [ + { + "name": "TestAbility", + "srcEntry": "./ets/testability/TestAbility.ets", + "description": "$string:TestAbility_desc", + "icon": "$media:icon", + "label": "$string:TestAbility_label", + "exported": true, + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "skills": [ + { + "actions": [ + "action.system.home" + ], + "entities": [ + "entity.system.home" + ] + } + ] + } + ] + } +} diff --git a/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/element/color.json b/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 000000000..3c712962d --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/element/string.json b/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 000000000..65d8fa5a7 --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_test_desc", + "value": "test ability description" + }, + { + "name": "TestAbility_desc", + "value": "the test ability" + }, + { + "name": "TestAbility_label", + "value": "test label" + } + ] +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/media/icon.png b/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 000000000..ce307a882 Binary files /dev/null and b/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/media/icon.png differ diff --git a/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json b/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 000000000..b7e7343ca --- /dev/null +++ b/tdesign-component/example/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "testability/pages/Index" + ] +} diff --git a/tdesign-component/example/ohos/hvigor/hvigor-config.json5 b/tdesign-component/example/ohos/hvigor/hvigor-config.json5 new file mode 100644 index 000000000..541ba3571 --- /dev/null +++ b/tdesign-component/example/ohos/hvigor/hvigor-config.json5 @@ -0,0 +1,20 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "modelVersion": "5.0.0", + "dependencies": { + } +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/hvigorfile.ts b/tdesign-component/example/ohos/hvigorfile.ts new file mode 100644 index 000000000..8f2d2aafe --- /dev/null +++ b/tdesign-component/example/ohos/hvigorfile.ts @@ -0,0 +1,21 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} \ No newline at end of file diff --git a/tdesign-component/example/ohos/oh-package.json5 b/tdesign-component/example/ohos/oh-package.json5 new file mode 100644 index 000000000..48d375d4c --- /dev/null +++ b/tdesign-component/example/ohos/oh-package.json5 @@ -0,0 +1,33 @@ +/* +* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd. +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +{ + "modelVersion": "5.0.0", + "name": "tdesign_flutter_example", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "@ohos/flutter_ohos": "file:./har/flutter.har" + }, + "devDependencies": { + "@ohos/hypium": "1.0.6" + }, + "overrides": { + "@ohos/flutter_ohos": "file:./har/flutter.har" + } +} diff --git a/tdesign-component/example/ohos/ohos-help.md b/tdesign-component/example/ohos/ohos-help.md new file mode 100644 index 000000000..932854d3d --- /dev/null +++ b/tdesign-component/example/ohos/ohos-help.md @@ -0,0 +1,28 @@ +# 环境变了配置 +```bash +# https://chromium.googlesource.com/chromium/tools/depot_tools.git +export PATH=~/development/depot_tools:$PATH + +export PUB_HOSTED_URL=https://pub.flutter-io.cn +export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn +export FLUTTER_GIT_URL=https://gitee.com/openharmony-sig/flutter_flutter.git + +# https://gitee.com/openharmony-sig/flutter_flutter.git +export PATH=$PATH:~/development/flutter_flutter/bin + +# HamonyOS SDK 需要申请华为开发者:https://juejin.cn/post/7355779183148875803 +# https://developer.huawei.com/consumer/cn/deveco-developer-suite/enabling/kit?currentPage=1&pageSize=100 +export TOOL_HOME=~/development/commandline-MacArm-5.0.3.402/command-line-tools +export DEVECO_SDK_HOME=$TOOL_HOME/sdk +export PATH=$TOOL_HOME/ohpm/bin:$PATH +export PATH=$TOOL_HOME/hvigor/bin:$PATH + +``` + +# 开发环境 +node16 +java jdk 17 +python3.11 +DevEco Studio NEXT Developer +https://gitee.com/openharmony-sig/flutter_engine/blob/master/README.md +https://gitee.com/openharmony-sig/flutter_flutter diff --git a/tdesign-component/example/pubspec.yaml b/tdesign-component/example/pubspec.yaml new file mode 100644 index 000000000..7cfb1dd4f --- /dev/null +++ b/tdesign-component/example/pubspec.yaml @@ -0,0 +1,127 @@ +name: tdesign_flutter_example +description: A new Flutter application. + +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +version: 1.0.0+1 + +environment: + sdk: ">=3.2.6 <4.0.0" + flutter: ">=3.16.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + web: ^0.3.0 + + #轮播图组件 + flutter_swiper_null_safety: ^1.0.2 + intl: any + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + flutter_markdown: ^0.6.14 + http: ^0.13.5 + tdesign_flutter: + path: ../ +# path_provider: 2.1.5 + extended_nested_scroll_view: any + flutter_screenutil: any +dev_dependencies: + flutter_test: + sdk: flutter + flutter_localizations: + sdk: flutter + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^1.0.0 + + +dependency_overrides: + flutter_plugin_android_lifecycle: 2.0.28 +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # 国际化配置 + generate: true # Add this line + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages + + assets: + - assets/api/ + - assets/code/ + - assets/version + - assets/img/ + - assets/theme.json + - assets/publish_time + +# 生成演示代码 +AOPMarket: + enable: false + compileTarget: frontend_server, dart2js + versions: + negativeInfinity~3.0.0: + unsupportedTips: 请在Flutter 3.+ 运行心悦工程 + 3.0.0~infinity: + path: ./tdesign_flutter_generator + saveDartDependencies: true diff --git a/tdesign-component/example/shell/Icons/add.svg b/tdesign-component/example/shell/Icons/add.svg new file mode 100644 index 000000000..51a44a8d6 --- /dev/null +++ b/tdesign-component/example/shell/Icons/add.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/add_circle.svg b/tdesign-component/example/shell/Icons/add_circle.svg new file mode 100644 index 000000000..7310c980b --- /dev/null +++ b/tdesign-component/example/shell/Icons/add_circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/add_rectangle.svg b/tdesign-component/example/shell/Icons/add_rectangle.svg new file mode 100644 index 000000000..6f6aa5a35 --- /dev/null +++ b/tdesign-component/example/shell/Icons/add_rectangle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/app.svg b/tdesign-component/example/shell/Icons/app.svg new file mode 100644 index 000000000..76c379693 --- /dev/null +++ b/tdesign-component/example/shell/Icons/app.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/arrow_down.svg b/tdesign-component/example/shell/Icons/arrow_down.svg new file mode 100644 index 000000000..5c32cd9a9 --- /dev/null +++ b/tdesign-component/example/shell/Icons/arrow_down.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/arrow_down_rectangle.svg b/tdesign-component/example/shell/Icons/arrow_down_rectangle.svg new file mode 100644 index 000000000..24f5b142c --- /dev/null +++ b/tdesign-component/example/shell/Icons/arrow_down_rectangle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/arrow_left.svg b/tdesign-component/example/shell/Icons/arrow_left.svg new file mode 100644 index 000000000..213397c52 --- /dev/null +++ b/tdesign-component/example/shell/Icons/arrow_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/arrow_right.svg b/tdesign-component/example/shell/Icons/arrow_right.svg new file mode 100644 index 000000000..b460888ea --- /dev/null +++ b/tdesign-component/example/shell/Icons/arrow_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/arrow_up.svg b/tdesign-component/example/shell/Icons/arrow_up.svg new file mode 100644 index 000000000..19e2a9667 --- /dev/null +++ b/tdesign-component/example/shell/Icons/arrow_up.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/attach.svg b/tdesign-component/example/shell/Icons/attach.svg new file mode 100644 index 000000000..b344730b7 --- /dev/null +++ b/tdesign-component/example/shell/Icons/attach.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/backtop.svg b/tdesign-component/example/shell/Icons/backtop.svg new file mode 100644 index 000000000..73ce35020 --- /dev/null +++ b/tdesign-component/example/shell/Icons/backtop.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/backtop_rectangle.svg b/tdesign-component/example/shell/Icons/backtop_rectangle.svg new file mode 100644 index 000000000..191d720d2 --- /dev/null +++ b/tdesign-component/example/shell/Icons/backtop_rectangle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/backward.svg b/tdesign-component/example/shell/Icons/backward.svg new file mode 100644 index 000000000..0167030b7 --- /dev/null +++ b/tdesign-component/example/shell/Icons/backward.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/barcode.svg b/tdesign-component/example/shell/Icons/barcode.svg new file mode 100644 index 000000000..f1fb3e9dd --- /dev/null +++ b/tdesign-component/example/shell/Icons/barcode.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/books.svg b/tdesign-component/example/shell/Icons/books.svg new file mode 100644 index 000000000..8a3714eed --- /dev/null +++ b/tdesign-component/example/shell/Icons/books.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/browse.svg b/tdesign-component/example/shell/Icons/browse.svg new file mode 100644 index 000000000..5afd830f6 --- /dev/null +++ b/tdesign-component/example/shell/Icons/browse.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/browse_off.svg b/tdesign-component/example/shell/Icons/browse_off.svg new file mode 100644 index 000000000..942a8c481 --- /dev/null +++ b/tdesign-component/example/shell/Icons/browse_off.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/bulletpoint.svg b/tdesign-component/example/shell/Icons/bulletpoint.svg new file mode 100644 index 000000000..f9b22718d --- /dev/null +++ b/tdesign-component/example/shell/Icons/bulletpoint.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tdesign-component/example/shell/Icons/calendar.svg b/tdesign-component/example/shell/Icons/calendar.svg new file mode 100644 index 000000000..00646511b --- /dev/null +++ b/tdesign-component/example/shell/Icons/calendar.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/call.svg b/tdesign-component/example/shell/Icons/call.svg new file mode 100644 index 000000000..402958047 --- /dev/null +++ b/tdesign-component/example/shell/Icons/call.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/caret_down.svg b/tdesign-component/example/shell/Icons/caret_down.svg new file mode 100644 index 000000000..20a14b6d4 --- /dev/null +++ b/tdesign-component/example/shell/Icons/caret_down.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/caret_down_small.svg b/tdesign-component/example/shell/Icons/caret_down_small.svg new file mode 100644 index 000000000..1e3420202 --- /dev/null +++ b/tdesign-component/example/shell/Icons/caret_down_small.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/caret_left.svg b/tdesign-component/example/shell/Icons/caret_left.svg new file mode 100644 index 000000000..ad53be21f --- /dev/null +++ b/tdesign-component/example/shell/Icons/caret_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/caret_left_small.svg b/tdesign-component/example/shell/Icons/caret_left_small.svg new file mode 100644 index 000000000..4f711ceee --- /dev/null +++ b/tdesign-component/example/shell/Icons/caret_left_small.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/caret_right.svg b/tdesign-component/example/shell/Icons/caret_right.svg new file mode 100644 index 000000000..2247a4887 --- /dev/null +++ b/tdesign-component/example/shell/Icons/caret_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/caret_right_small.svg b/tdesign-component/example/shell/Icons/caret_right_small.svg new file mode 100644 index 000000000..610d8676e --- /dev/null +++ b/tdesign-component/example/shell/Icons/caret_right_small.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/caret_up.svg b/tdesign-component/example/shell/Icons/caret_up.svg new file mode 100644 index 000000000..e811196c5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/caret_up.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/caret_up_small.svg b/tdesign-component/example/shell/Icons/caret_up_small.svg new file mode 100644 index 000000000..a2de36097 --- /dev/null +++ b/tdesign-component/example/shell/Icons/caret_up_small.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/cart.svg b/tdesign-component/example/shell/Icons/cart.svg new file mode 100644 index 000000000..694c3813c --- /dev/null +++ b/tdesign-component/example/shell/Icons/cart.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/chart.svg b/tdesign-component/example/shell/Icons/chart.svg new file mode 100644 index 000000000..ea56198b5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chart.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/chart_bar.svg b/tdesign-component/example/shell/Icons/chart_bar.svg new file mode 100644 index 000000000..cdb2115f1 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chart_bar.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/chart_bubble.svg b/tdesign-component/example/shell/Icons/chart_bubble.svg new file mode 100644 index 000000000..4a7974a18 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chart_bubble.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/chart_pie.svg b/tdesign-component/example/shell/Icons/chart_pie.svg new file mode 100644 index 000000000..823328303 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chart_pie.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/chat.svg b/tdesign-component/example/shell/Icons/chat.svg new file mode 100644 index 000000000..b7f159496 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/check.svg b/tdesign-component/example/shell/Icons/check.svg new file mode 100644 index 000000000..91f5a1aa1 --- /dev/null +++ b/tdesign-component/example/shell/Icons/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/check_circle.svg b/tdesign-component/example/shell/Icons/check_circle.svg new file mode 100644 index 000000000..b711e4d45 --- /dev/null +++ b/tdesign-component/example/shell/Icons/check_circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/check_circle_filled.svg b/tdesign-component/example/shell/Icons/check_circle_filled.svg new file mode 100644 index 000000000..97d724c63 --- /dev/null +++ b/tdesign-component/example/shell/Icons/check_circle_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/check_rectangle.svg b/tdesign-component/example/shell/Icons/check_rectangle.svg new file mode 100644 index 000000000..e02d6fc19 --- /dev/null +++ b/tdesign-component/example/shell/Icons/check_rectangle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/check_rectangle_filled.svg b/tdesign-component/example/shell/Icons/check_rectangle_filled.svg new file mode 100644 index 000000000..c70eba628 --- /dev/null +++ b/tdesign-component/example/shell/Icons/check_rectangle_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/chevron_down.svg b/tdesign-component/example/shell/Icons/chevron_down.svg new file mode 100644 index 000000000..8f04103ca --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_down.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/chevron_down_circle.svg b/tdesign-component/example/shell/Icons/chevron_down_circle.svg new file mode 100644 index 000000000..ecec6c15b --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_down_circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/chevron_down_rectangle.svg b/tdesign-component/example/shell/Icons/chevron_down_rectangle.svg new file mode 100644 index 000000000..aff89c995 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_down_rectangle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/chevron_left.double.svg b/tdesign-component/example/shell/Icons/chevron_left.double.svg new file mode 100644 index 000000000..b448a2b74 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_left.double.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/chevron_left.svg b/tdesign-component/example/shell/Icons/chevron_left.svg new file mode 100644 index 000000000..92dd181a5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/chevron_left_circle.svg b/tdesign-component/example/shell/Icons/chevron_left_circle.svg new file mode 100644 index 000000000..86f9204be --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_left_circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/chevron_left_rectangle.svg b/tdesign-component/example/shell/Icons/chevron_left_rectangle.svg new file mode 100644 index 000000000..98b0c1099 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_left_rectangle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/chevron_right.double.svg b/tdesign-component/example/shell/Icons/chevron_right.double.svg new file mode 100644 index 000000000..1b936c082 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_right.double.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/chevron_right.svg b/tdesign-component/example/shell/Icons/chevron_right.svg new file mode 100644 index 000000000..12722f634 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/chevron_right_circle.svg b/tdesign-component/example/shell/Icons/chevron_right_circle.svg new file mode 100644 index 000000000..85973a5f7 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_right_circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/chevron_right_rectangle.svg b/tdesign-component/example/shell/Icons/chevron_right_rectangle.svg new file mode 100644 index 000000000..f249552cc --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_right_rectangle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/chevron_up.svg b/tdesign-component/example/shell/Icons/chevron_up.svg new file mode 100644 index 000000000..735ce74a5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_up.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/chevron_up_circle.svg b/tdesign-component/example/shell/Icons/chevron_up_circle.svg new file mode 100644 index 000000000..9be85e718 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_up_circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/chevron_up_rectangle.svg b/tdesign-component/example/shell/Icons/chevron_up_rectangle.svg new file mode 100644 index 000000000..e85cbb671 --- /dev/null +++ b/tdesign-component/example/shell/Icons/chevron_up_rectangle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/circle.svg b/tdesign-component/example/shell/Icons/circle.svg new file mode 100644 index 000000000..53ed79d2f --- /dev/null +++ b/tdesign-component/example/shell/Icons/circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/clear.svg b/tdesign-component/example/shell/Icons/clear.svg new file mode 100644 index 000000000..89c95be0e --- /dev/null +++ b/tdesign-component/example/shell/Icons/clear.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/close.svg b/tdesign-component/example/shell/Icons/close.svg new file mode 100644 index 000000000..30e1b43ae --- /dev/null +++ b/tdesign-component/example/shell/Icons/close.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/close_circle.svg b/tdesign-component/example/shell/Icons/close_circle.svg new file mode 100644 index 000000000..4b7d3cec4 --- /dev/null +++ b/tdesign-component/example/shell/Icons/close_circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/close_circle_filled.svg b/tdesign-component/example/shell/Icons/close_circle_filled.svg new file mode 100644 index 000000000..451b729c0 --- /dev/null +++ b/tdesign-component/example/shell/Icons/close_circle_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/close_rectangle.svg b/tdesign-component/example/shell/Icons/close_rectangle.svg new file mode 100644 index 000000000..ec723a94f --- /dev/null +++ b/tdesign-component/example/shell/Icons/close_rectangle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/cloud.svg b/tdesign-component/example/shell/Icons/cloud.svg new file mode 100644 index 000000000..e679ad232 --- /dev/null +++ b/tdesign-component/example/shell/Icons/cloud.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/cloud_download.svg b/tdesign-component/example/shell/Icons/cloud_download.svg new file mode 100644 index 000000000..2e343f3ff --- /dev/null +++ b/tdesign-component/example/shell/Icons/cloud_download.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/cloud_upload.svg b/tdesign-component/example/shell/Icons/cloud_upload.svg new file mode 100644 index 000000000..1dbe526d0 --- /dev/null +++ b/tdesign-component/example/shell/Icons/cloud_upload.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/code.svg b/tdesign-component/example/shell/Icons/code.svg new file mode 100644 index 000000000..9b28131f9 --- /dev/null +++ b/tdesign-component/example/shell/Icons/code.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/control.platform.svg b/tdesign-component/example/shell/Icons/control.platform.svg new file mode 100644 index 000000000..e34d393b6 --- /dev/null +++ b/tdesign-component/example/shell/Icons/control.platform.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/creditcard.svg b/tdesign-component/example/shell/Icons/creditcard.svg new file mode 100644 index 000000000..07675b234 --- /dev/null +++ b/tdesign-component/example/shell/Icons/creditcard.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/dashboard.svg b/tdesign-component/example/shell/Icons/dashboard.svg new file mode 100644 index 000000000..cedc4b1f7 --- /dev/null +++ b/tdesign-component/example/shell/Icons/dashboard.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/delete.svg b/tdesign-component/example/shell/Icons/delete.svg new file mode 100644 index 000000000..c79c9bf3f --- /dev/null +++ b/tdesign-component/example/shell/Icons/delete.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/desktop.svg b/tdesign-component/example/shell/Icons/desktop.svg new file mode 100644 index 000000000..449dbda7e --- /dev/null +++ b/tdesign-component/example/shell/Icons/desktop.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/discount.svg b/tdesign-component/example/shell/Icons/discount.svg new file mode 100644 index 000000000..908bbb1b3 --- /dev/null +++ b/tdesign-component/example/shell/Icons/discount.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/discount_filled.svg b/tdesign-component/example/shell/Icons/discount_filled.svg new file mode 100644 index 000000000..92f2bc601 --- /dev/null +++ b/tdesign-component/example/shell/Icons/discount_filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/download.svg b/tdesign-component/example/shell/Icons/download.svg new file mode 100644 index 000000000..8140b0ffa --- /dev/null +++ b/tdesign-component/example/shell/Icons/download.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/edit.svg b/tdesign-component/example/shell/Icons/edit.svg new file mode 100644 index 000000000..3c2afc977 --- /dev/null +++ b/tdesign-component/example/shell/Icons/edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/edit_1.svg b/tdesign-component/example/shell/Icons/edit_1.svg new file mode 100644 index 000000000..5f77eecd4 --- /dev/null +++ b/tdesign-component/example/shell/Icons/edit_1.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tdesign-component/example/shell/Icons/ellipsis.svg b/tdesign-component/example/shell/Icons/ellipsis.svg new file mode 100644 index 000000000..7858b0417 --- /dev/null +++ b/tdesign-component/example/shell/Icons/ellipsis.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/enter.svg b/tdesign-component/example/shell/Icons/enter.svg new file mode 100644 index 000000000..40efa12cb --- /dev/null +++ b/tdesign-component/example/shell/Icons/enter.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/error.svg b/tdesign-component/example/shell/Icons/error.svg new file mode 100644 index 000000000..6cac9b11e --- /dev/null +++ b/tdesign-component/example/shell/Icons/error.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/error_circle.svg b/tdesign-component/example/shell/Icons/error_circle.svg new file mode 100644 index 000000000..f8e995146 --- /dev/null +++ b/tdesign-component/example/shell/Icons/error_circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/error_circle_filled.svg b/tdesign-component/example/shell/Icons/error_circle_filled.svg new file mode 100644 index 000000000..200d8bc95 --- /dev/null +++ b/tdesign-component/example/shell/Icons/error_circle_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/file.svg b/tdesign-component/example/shell/Icons/file.svg new file mode 100644 index 000000000..a7d69139a --- /dev/null +++ b/tdesign-component/example/shell/Icons/file.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/file_add.svg b/tdesign-component/example/shell/Icons/file_add.svg new file mode 100644 index 000000000..f023ddc0c --- /dev/null +++ b/tdesign-component/example/shell/Icons/file_add.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/file_copy.svg b/tdesign-component/example/shell/Icons/file_copy.svg new file mode 100644 index 000000000..b273b64ac --- /dev/null +++ b/tdesign-component/example/shell/Icons/file_copy.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/file_excel.svg b/tdesign-component/example/shell/Icons/file_excel.svg new file mode 100644 index 000000000..327abf2b4 --- /dev/null +++ b/tdesign-component/example/shell/Icons/file_excel.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/file_icon.svg b/tdesign-component/example/shell/Icons/file_icon.svg new file mode 100644 index 000000000..33a9c920e --- /dev/null +++ b/tdesign-component/example/shell/Icons/file_icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tdesign-component/example/shell/Icons/file_image.svg b/tdesign-component/example/shell/Icons/file_image.svg new file mode 100644 index 000000000..4a548bb9c --- /dev/null +++ b/tdesign-component/example/shell/Icons/file_image.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/file_paste.svg b/tdesign-component/example/shell/Icons/file_paste.svg new file mode 100644 index 000000000..c91e45c87 --- /dev/null +++ b/tdesign-component/example/shell/Icons/file_paste.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/file_pdf.svg b/tdesign-component/example/shell/Icons/file_pdf.svg new file mode 100644 index 000000000..e70016bc6 --- /dev/null +++ b/tdesign-component/example/shell/Icons/file_pdf.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/file_powerpoint.svg b/tdesign-component/example/shell/Icons/file_powerpoint.svg new file mode 100644 index 000000000..d25424456 --- /dev/null +++ b/tdesign-component/example/shell/Icons/file_powerpoint.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/file_unknown.svg b/tdesign-component/example/shell/Icons/file_unknown.svg new file mode 100644 index 000000000..cd0c056a5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/file_unknown.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/file_word.svg b/tdesign-component/example/shell/Icons/file_word.svg new file mode 100644 index 000000000..c783a1dc9 --- /dev/null +++ b/tdesign-component/example/shell/Icons/file_word.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/filter.svg b/tdesign-component/example/shell/Icons/filter.svg new file mode 100644 index 000000000..a3f5bc807 --- /dev/null +++ b/tdesign-component/example/shell/Icons/filter.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/filter_clear.svg b/tdesign-component/example/shell/Icons/filter_clear.svg new file mode 100644 index 000000000..fc4e7bd85 --- /dev/null +++ b/tdesign-component/example/shell/Icons/filter_clear.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/flag.svg b/tdesign-component/example/shell/Icons/flag.svg new file mode 100644 index 000000000..1272e5519 --- /dev/null +++ b/tdesign-component/example/shell/Icons/flag.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/folder.svg b/tdesign-component/example/shell/Icons/folder.svg new file mode 100644 index 000000000..536000679 --- /dev/null +++ b/tdesign-component/example/shell/Icons/folder.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/folder_add.svg b/tdesign-component/example/shell/Icons/folder_add.svg new file mode 100644 index 000000000..b94b91808 --- /dev/null +++ b/tdesign-component/example/shell/Icons/folder_add.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/folder_open.svg b/tdesign-component/example/shell/Icons/folder_open.svg new file mode 100644 index 000000000..f829e85ce --- /dev/null +++ b/tdesign-component/example/shell/Icons/folder_open.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/fork.svg b/tdesign-component/example/shell/Icons/fork.svg new file mode 100644 index 000000000..5996090b5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/fork.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/format_horizontal_align_bottom.svg b/tdesign-component/example/shell/Icons/format_horizontal_align_bottom.svg new file mode 100644 index 000000000..920405228 --- /dev/null +++ b/tdesign-component/example/shell/Icons/format_horizontal_align_bottom.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/format_horizontal_align_center.svg b/tdesign-component/example/shell/Icons/format_horizontal_align_center.svg new file mode 100644 index 000000000..e2a3e4418 --- /dev/null +++ b/tdesign-component/example/shell/Icons/format_horizontal_align_center.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/format_horizontal_align_top.svg b/tdesign-component/example/shell/Icons/format_horizontal_align_top.svg new file mode 100644 index 000000000..2f2768f14 --- /dev/null +++ b/tdesign-component/example/shell/Icons/format_horizontal_align_top.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/format_vertical_align_center.svg b/tdesign-component/example/shell/Icons/format_vertical_align_center.svg new file mode 100644 index 000000000..c686906c2 --- /dev/null +++ b/tdesign-component/example/shell/Icons/format_vertical_align_center.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/format_vertical_align_left.svg b/tdesign-component/example/shell/Icons/format_vertical_align_left.svg new file mode 100644 index 000000000..0235c732a --- /dev/null +++ b/tdesign-component/example/shell/Icons/format_vertical_align_left.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/format_vertical_align_right.svg b/tdesign-component/example/shell/Icons/format_vertical_align_right.svg new file mode 100644 index 000000000..26515bb5d --- /dev/null +++ b/tdesign-component/example/shell/Icons/format_vertical_align_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/forward.svg b/tdesign-component/example/shell/Icons/forward.svg new file mode 100644 index 000000000..913b495ba --- /dev/null +++ b/tdesign-component/example/shell/Icons/forward.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/fullscreen_exit.svg b/tdesign-component/example/shell/Icons/fullscreen_exit.svg new file mode 100644 index 000000000..3e3e453b3 --- /dev/null +++ b/tdesign-component/example/shell/Icons/fullscreen_exit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/fullsreen.svg b/tdesign-component/example/shell/Icons/fullsreen.svg new file mode 100644 index 000000000..4991a4c15 --- /dev/null +++ b/tdesign-component/example/shell/Icons/fullsreen.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/gender_female.svg b/tdesign-component/example/shell/Icons/gender_female.svg new file mode 100644 index 000000000..5fc7f7c66 --- /dev/null +++ b/tdesign-component/example/shell/Icons/gender_female.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/gender_male.svg b/tdesign-component/example/shell/Icons/gender_male.svg new file mode 100644 index 000000000..3df04e6af --- /dev/null +++ b/tdesign-component/example/shell/Icons/gender_male.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/gift.svg b/tdesign-component/example/shell/Icons/gift.svg new file mode 100644 index 000000000..f365334a8 --- /dev/null +++ b/tdesign-component/example/shell/Icons/gift.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/heart.svg b/tdesign-component/example/shell/Icons/heart.svg new file mode 100644 index 000000000..5b217717c --- /dev/null +++ b/tdesign-component/example/shell/Icons/heart.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/heart_filled.svg b/tdesign-component/example/shell/Icons/heart_filled.svg new file mode 100644 index 000000000..69a48a8e2 --- /dev/null +++ b/tdesign-component/example/shell/Icons/heart_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/help.svg b/tdesign-component/example/shell/Icons/help.svg new file mode 100644 index 000000000..707d58719 --- /dev/null +++ b/tdesign-component/example/shell/Icons/help.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/help_circle.svg b/tdesign-component/example/shell/Icons/help_circle.svg new file mode 100644 index 000000000..eaab31354 --- /dev/null +++ b/tdesign-component/example/shell/Icons/help_circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/help_circle_filled.svg b/tdesign-component/example/shell/Icons/help_circle_filled.svg new file mode 100644 index 000000000..950b27922 --- /dev/null +++ b/tdesign-component/example/shell/Icons/help_circle_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/history.svg b/tdesign-component/example/shell/Icons/history.svg new file mode 100644 index 000000000..84b8fc525 --- /dev/null +++ b/tdesign-component/example/shell/Icons/history.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/home.svg b/tdesign-component/example/shell/Icons/home.svg new file mode 100644 index 000000000..0e55052f5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/home.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/hourglass.svg b/tdesign-component/example/shell/Icons/hourglass.svg new file mode 100644 index 000000000..e7e8f27bf --- /dev/null +++ b/tdesign-component/example/shell/Icons/hourglass.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/image.svg b/tdesign-component/example/shell/Icons/image.svg new file mode 100644 index 000000000..eb4a04150 --- /dev/null +++ b/tdesign-component/example/shell/Icons/image.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/image_error.svg b/tdesign-component/example/shell/Icons/image_error.svg new file mode 100644 index 000000000..8c47e5e78 --- /dev/null +++ b/tdesign-component/example/shell/Icons/image_error.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/info_circle.svg b/tdesign-component/example/shell/Icons/info_circle.svg new file mode 100644 index 000000000..6e3804382 --- /dev/null +++ b/tdesign-component/example/shell/Icons/info_circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/info_circle_filled.svg b/tdesign-component/example/shell/Icons/info_circle_filled.svg new file mode 100644 index 000000000..ca9b9ba23 --- /dev/null +++ b/tdesign-component/example/shell/Icons/info_circle_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/internet.svg b/tdesign-component/example/shell/Icons/internet.svg new file mode 100644 index 000000000..0ba9ab570 --- /dev/null +++ b/tdesign-component/example/shell/Icons/internet.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/jump.svg b/tdesign-component/example/shell/Icons/jump.svg new file mode 100644 index 000000000..181347260 --- /dev/null +++ b/tdesign-component/example/shell/Icons/jump.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/laptop.svg b/tdesign-component/example/shell/Icons/laptop.svg new file mode 100644 index 000000000..d645b6e69 --- /dev/null +++ b/tdesign-component/example/shell/Icons/laptop.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/layers.svg b/tdesign-component/example/shell/Icons/layers.svg new file mode 100644 index 000000000..3baa696a9 --- /dev/null +++ b/tdesign-component/example/shell/Icons/layers.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/link.svg b/tdesign-component/example/shell/Icons/link.svg new file mode 100644 index 000000000..7e226f628 --- /dev/null +++ b/tdesign-component/example/shell/Icons/link.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/link_unlink.svg b/tdesign-component/example/shell/Icons/link_unlink.svg new file mode 100644 index 000000000..ab1ff0971 --- /dev/null +++ b/tdesign-component/example/shell/Icons/link_unlink.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/tdesign-component/example/shell/Icons/location.svg b/tdesign-component/example/shell/Icons/location.svg new file mode 100644 index 000000000..cfcc5cd82 --- /dev/null +++ b/tdesign-component/example/shell/Icons/location.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/lock_off.svg b/tdesign-component/example/shell/Icons/lock_off.svg new file mode 100644 index 000000000..7aa501bec --- /dev/null +++ b/tdesign-component/example/shell/Icons/lock_off.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/lock_on.svg b/tdesign-component/example/shell/Icons/lock_on.svg new file mode 100644 index 000000000..b6a39b855 --- /dev/null +++ b/tdesign-component/example/shell/Icons/lock_on.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/login.svg b/tdesign-component/example/shell/Icons/login.svg new file mode 100644 index 000000000..6d9d7703c --- /dev/null +++ b/tdesign-component/example/shell/Icons/login.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/logo_android.svg b/tdesign-component/example/shell/Icons/logo_android.svg new file mode 100644 index 000000000..6b30ec7fd --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_android.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/logo_apple.svg b/tdesign-component/example/shell/Icons/logo_apple.svg new file mode 100644 index 000000000..aaa31cd13 --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_apple.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/logo_apple_filled.svg b/tdesign-component/example/shell/Icons/logo_apple_filled.svg new file mode 100644 index 000000000..40fddb932 --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_apple_filled.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/logo_chrome.svg b/tdesign-component/example/shell/Icons/logo_chrome.svg new file mode 100644 index 000000000..bf1ead0a7 --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_chrome.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/logo_chrome_filled.svg b/tdesign-component/example/shell/Icons/logo_chrome_filled.svg new file mode 100644 index 000000000..9dabbeb96 --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_chrome_filled.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/logo_codepen.svg b/tdesign-component/example/shell/Icons/logo_codepen.svg new file mode 100644 index 000000000..8f860b52f --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_codepen.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/logo_github.svg b/tdesign-component/example/shell/Icons/logo_github.svg new file mode 100644 index 000000000..98b4ddc23 --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_github.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/logo_github_filled.svg b/tdesign-component/example/shell/Icons/logo_github_filled.svg new file mode 100644 index 000000000..72bcdacae --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_github_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/logo_ie.svg b/tdesign-component/example/shell/Icons/logo_ie.svg new file mode 100644 index 000000000..21a96b702 --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_ie.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/logo_ie_filled.svg b/tdesign-component/example/shell/Icons/logo_ie_filled.svg new file mode 100644 index 000000000..09ce8f406 --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_ie_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/logo_qq.svg b/tdesign-component/example/shell/Icons/logo_qq.svg new file mode 100644 index 000000000..1e44a64ce --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_qq.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/logo_wechat.svg b/tdesign-component/example/shell/Icons/logo_wechat.svg new file mode 100644 index 000000000..dbdfab31a --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_wechat.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/logo_wecom.svg b/tdesign-component/example/shell/Icons/logo_wecom.svg new file mode 100644 index 000000000..190568457 --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_wecom.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tdesign-component/example/shell/Icons/logo_windows.svg b/tdesign-component/example/shell/Icons/logo_windows.svg new file mode 100644 index 000000000..a40d69dbe --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_windows.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/logo_windows_filled.svg b/tdesign-component/example/shell/Icons/logo_windows_filled.svg new file mode 100644 index 000000000..db3a3650c --- /dev/null +++ b/tdesign-component/example/shell/Icons/logo_windows_filled.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/logout.svg b/tdesign-component/example/shell/Icons/logout.svg new file mode 100644 index 000000000..184374bc4 --- /dev/null +++ b/tdesign-component/example/shell/Icons/logout.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/mail.svg b/tdesign-component/example/shell/Icons/mail.svg new file mode 100644 index 000000000..404a982c8 --- /dev/null +++ b/tdesign-component/example/shell/Icons/mail.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/menu_application.svg b/tdesign-component/example/shell/Icons/menu_application.svg new file mode 100644 index 000000000..e1850f931 --- /dev/null +++ b/tdesign-component/example/shell/Icons/menu_application.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/tdesign-component/example/shell/Icons/menu_fold.svg b/tdesign-component/example/shell/Icons/menu_fold.svg new file mode 100644 index 000000000..31a43bf1e --- /dev/null +++ b/tdesign-component/example/shell/Icons/menu_fold.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/menu_unfold.svg b/tdesign-component/example/shell/Icons/menu_unfold.svg new file mode 100644 index 000000000..d5a479094 --- /dev/null +++ b/tdesign-component/example/shell/Icons/menu_unfold.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/minus_circle.svg b/tdesign-component/example/shell/Icons/minus_circle.svg new file mode 100644 index 000000000..bdf10091e --- /dev/null +++ b/tdesign-component/example/shell/Icons/minus_circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/minus_circle_filled.svg b/tdesign-component/example/shell/Icons/minus_circle_filled.svg new file mode 100644 index 000000000..470351510 --- /dev/null +++ b/tdesign-component/example/shell/Icons/minus_circle_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/minus_rectangle.svg b/tdesign-component/example/shell/Icons/minus_rectangle.svg new file mode 100644 index 000000000..0b10ed40f --- /dev/null +++ b/tdesign-component/example/shell/Icons/minus_rectangle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/mirror.svg b/tdesign-component/example/shell/Icons/mirror.svg new file mode 100644 index 000000000..076ff9fc5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/mirror.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/mobile.svg b/tdesign-component/example/shell/Icons/mobile.svg new file mode 100644 index 000000000..1b0541009 --- /dev/null +++ b/tdesign-component/example/shell/Icons/mobile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/mobile_vibrate.svg b/tdesign-component/example/shell/Icons/mobile_vibrate.svg new file mode 100644 index 000000000..77bf5dbc5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/mobile_vibrate.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/money_circle.svg b/tdesign-component/example/shell/Icons/money_circle.svg new file mode 100644 index 000000000..2521af167 --- /dev/null +++ b/tdesign-component/example/shell/Icons/money_circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/more.svg b/tdesign-component/example/shell/Icons/more.svg new file mode 100644 index 000000000..b983a477a --- /dev/null +++ b/tdesign-component/example/shell/Icons/more.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/move.svg b/tdesign-component/example/shell/Icons/move.svg new file mode 100644 index 000000000..9a40ca2c3 --- /dev/null +++ b/tdesign-component/example/shell/Icons/move.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/tdesign-component/example/shell/Icons/next.svg b/tdesign-component/example/shell/Icons/next.svg new file mode 100644 index 000000000..419ba88c7 --- /dev/null +++ b/tdesign-component/example/shell/Icons/next.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/notification.svg b/tdesign-component/example/shell/Icons/notification.svg new file mode 100644 index 000000000..b1f6be90d --- /dev/null +++ b/tdesign-component/example/shell/Icons/notification.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/notification_filled.svg b/tdesign-component/example/shell/Icons/notification_filled.svg new file mode 100644 index 000000000..da30815ff --- /dev/null +++ b/tdesign-component/example/shell/Icons/notification_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/order_adjustment.column.svg b/tdesign-component/example/shell/Icons/order_adjustment.column.svg new file mode 100644 index 000000000..546346714 --- /dev/null +++ b/tdesign-component/example/shell/Icons/order_adjustment.column.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/order_ascending.svg b/tdesign-component/example/shell/Icons/order_ascending.svg new file mode 100644 index 000000000..00a41d7c8 --- /dev/null +++ b/tdesign-component/example/shell/Icons/order_ascending.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/order_descending.svg b/tdesign-component/example/shell/Icons/order_descending.svg new file mode 100644 index 000000000..e020ed056 --- /dev/null +++ b/tdesign-component/example/shell/Icons/order_descending.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/page_first.svg b/tdesign-component/example/shell/Icons/page_first.svg new file mode 100644 index 000000000..76634e612 --- /dev/null +++ b/tdesign-component/example/shell/Icons/page_first.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/page_last.svg b/tdesign-component/example/shell/Icons/page_last.svg new file mode 100644 index 000000000..e2c9c4655 --- /dev/null +++ b/tdesign-component/example/shell/Icons/page_last.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/pause_circle_filled.svg b/tdesign-component/example/shell/Icons/pause_circle_filled.svg new file mode 100644 index 000000000..986ca1781 --- /dev/null +++ b/tdesign-component/example/shell/Icons/pause_circle_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/photo.svg b/tdesign-component/example/shell/Icons/photo.svg new file mode 100644 index 000000000..aa758cb94 --- /dev/null +++ b/tdesign-component/example/shell/Icons/photo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/pin.svg b/tdesign-component/example/shell/Icons/pin.svg new file mode 100644 index 000000000..d4df5bf10 --- /dev/null +++ b/tdesign-component/example/shell/Icons/pin.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/pin_filled.svg b/tdesign-component/example/shell/Icons/pin_filled.svg new file mode 100644 index 000000000..54f7190ec --- /dev/null +++ b/tdesign-component/example/shell/Icons/pin_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/play.svg b/tdesign-component/example/shell/Icons/play.svg new file mode 100644 index 000000000..ca804fe60 --- /dev/null +++ b/tdesign-component/example/shell/Icons/play.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/play_circle.svg b/tdesign-component/example/shell/Icons/play_circle.svg new file mode 100644 index 000000000..68d3dfb0d --- /dev/null +++ b/tdesign-component/example/shell/Icons/play_circle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/play_circle_filled.svg b/tdesign-component/example/shell/Icons/play_circle_filled.svg new file mode 100644 index 000000000..99545a1c9 --- /dev/null +++ b/tdesign-component/example/shell/Icons/play_circle_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/play_circle_stroke.svg b/tdesign-component/example/shell/Icons/play_circle_stroke.svg new file mode 100644 index 000000000..93ef1c6cf --- /dev/null +++ b/tdesign-component/example/shell/Icons/play_circle_stroke.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/poweroff.svg b/tdesign-component/example/shell/Icons/poweroff.svg new file mode 100644 index 000000000..4dfd82d14 --- /dev/null +++ b/tdesign-component/example/shell/Icons/poweroff.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/precise.monitor.svg b/tdesign-component/example/shell/Icons/precise.monitor.svg new file mode 100644 index 000000000..b38b2b169 --- /dev/null +++ b/tdesign-component/example/shell/Icons/precise.monitor.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/previous.svg b/tdesign-component/example/shell/Icons/previous.svg new file mode 100644 index 000000000..422e0c225 --- /dev/null +++ b/tdesign-component/example/shell/Icons/previous.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/print.svg b/tdesign-component/example/shell/Icons/print.svg new file mode 100644 index 000000000..82afbae25 --- /dev/null +++ b/tdesign-component/example/shell/Icons/print.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/qrcode.svg b/tdesign-component/example/shell/Icons/qrcode.svg new file mode 100644 index 000000000..3969c584c --- /dev/null +++ b/tdesign-component/example/shell/Icons/qrcode.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/tdesign-component/example/shell/Icons/queue.svg b/tdesign-component/example/shell/Icons/queue.svg new file mode 100644 index 000000000..84007bc47 --- /dev/null +++ b/tdesign-component/example/shell/Icons/queue.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/rectangle.svg b/tdesign-component/example/shell/Icons/rectangle.svg new file mode 100644 index 000000000..cd51dacfd --- /dev/null +++ b/tdesign-component/example/shell/Icons/rectangle.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/refresh.svg b/tdesign-component/example/shell/Icons/refresh.svg new file mode 100644 index 000000000..62fbd366c --- /dev/null +++ b/tdesign-component/example/shell/Icons/refresh.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/relativity.svg b/tdesign-component/example/shell/Icons/relativity.svg new file mode 100644 index 000000000..e374d63e8 --- /dev/null +++ b/tdesign-component/example/shell/Icons/relativity.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/remove.svg b/tdesign-component/example/shell/Icons/remove.svg new file mode 100644 index 000000000..725e07877 --- /dev/null +++ b/tdesign-component/example/shell/Icons/remove.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/rollback.svg b/tdesign-component/example/shell/Icons/rollback.svg new file mode 100644 index 000000000..04f82d6d3 --- /dev/null +++ b/tdesign-component/example/shell/Icons/rollback.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/root_list.svg b/tdesign-component/example/shell/Icons/root_list.svg new file mode 100644 index 000000000..4ce46b4e5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/root_list.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tdesign-component/example/shell/Icons/rotation.svg b/tdesign-component/example/shell/Icons/rotation.svg new file mode 100644 index 000000000..7a8bda9e7 --- /dev/null +++ b/tdesign-component/example/shell/Icons/rotation.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/round.svg b/tdesign-component/example/shell/Icons/round.svg new file mode 100644 index 000000000..018403b86 --- /dev/null +++ b/tdesign-component/example/shell/Icons/round.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/save.svg b/tdesign-component/example/shell/Icons/save.svg new file mode 100644 index 000000000..a0d39a52d --- /dev/null +++ b/tdesign-component/example/shell/Icons/save.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/scan.svg b/tdesign-component/example/shell/Icons/scan.svg new file mode 100644 index 000000000..c7c1cbc34 --- /dev/null +++ b/tdesign-component/example/shell/Icons/scan.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/search.svg b/tdesign-component/example/shell/Icons/search.svg new file mode 100644 index 000000000..c76c31f2a --- /dev/null +++ b/tdesign-component/example/shell/Icons/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/secured.svg b/tdesign-component/example/shell/Icons/secured.svg new file mode 100644 index 000000000..0887667ae --- /dev/null +++ b/tdesign-component/example/shell/Icons/secured.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/server.svg b/tdesign-component/example/shell/Icons/server.svg new file mode 100644 index 000000000..29793fe9e --- /dev/null +++ b/tdesign-component/example/shell/Icons/server.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/service.svg b/tdesign-component/example/shell/Icons/service.svg new file mode 100644 index 000000000..887df4ff4 --- /dev/null +++ b/tdesign-component/example/shell/Icons/service.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/setting.svg b/tdesign-component/example/shell/Icons/setting.svg new file mode 100644 index 000000000..235b83ff6 --- /dev/null +++ b/tdesign-component/example/shell/Icons/setting.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/share.svg b/tdesign-component/example/shell/Icons/share.svg new file mode 100644 index 000000000..8e606988b --- /dev/null +++ b/tdesign-component/example/shell/Icons/share.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/shop.svg b/tdesign-component/example/shell/Icons/shop.svg new file mode 100644 index 000000000..e6ab0e433 --- /dev/null +++ b/tdesign-component/example/shell/Icons/shop.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/slash.svg b/tdesign-component/example/shell/Icons/slash.svg new file mode 100644 index 000000000..ec045700a --- /dev/null +++ b/tdesign-component/example/shell/Icons/slash.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/sound.svg b/tdesign-component/example/shell/Icons/sound.svg new file mode 100644 index 000000000..7a14d1081 --- /dev/null +++ b/tdesign-component/example/shell/Icons/sound.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/star.svg b/tdesign-component/example/shell/Icons/star.svg new file mode 100644 index 000000000..2adaa03ba --- /dev/null +++ b/tdesign-component/example/shell/Icons/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/star_filled.svg b/tdesign-component/example/shell/Icons/star_filled.svg new file mode 100644 index 000000000..147d15c47 --- /dev/null +++ b/tdesign-component/example/shell/Icons/star_filled.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/tdesign-component/example/shell/Icons/stop.svg b/tdesign-component/example/shell/Icons/stop.svg new file mode 100644 index 000000000..c1b82aab5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/stop.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/stop_circle.svg b/tdesign-component/example/shell/Icons/stop_circle.svg new file mode 100644 index 000000000..ac7a4accb --- /dev/null +++ b/tdesign-component/example/shell/Icons/stop_circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/stop_circle_1.svg b/tdesign-component/example/shell/Icons/stop_circle_1.svg new file mode 100644 index 000000000..a9cd55a27 --- /dev/null +++ b/tdesign-component/example/shell/Icons/stop_circle_1.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/stop_circle_filled.svg b/tdesign-component/example/shell/Icons/stop_circle_filled.svg new file mode 100644 index 000000000..e5df66245 --- /dev/null +++ b/tdesign-component/example/shell/Icons/stop_circle_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/swap.svg b/tdesign-component/example/shell/Icons/swap.svg new file mode 100644 index 000000000..6bb7d25db --- /dev/null +++ b/tdesign-component/example/shell/Icons/swap.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/swap_left.svg b/tdesign-component/example/shell/Icons/swap_left.svg new file mode 100644 index 000000000..c91ed2fd5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/swap_left.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/swap_right.svg b/tdesign-component/example/shell/Icons/swap_right.svg new file mode 100644 index 000000000..16638fae5 --- /dev/null +++ b/tdesign-component/example/shell/Icons/swap_right.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/thumb_down.svg b/tdesign-component/example/shell/Icons/thumb_down.svg new file mode 100644 index 000000000..48caf71b7 --- /dev/null +++ b/tdesign-component/example/shell/Icons/thumb_down.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/thumb_up.svg b/tdesign-component/example/shell/Icons/thumb_up.svg new file mode 100644 index 000000000..717226e4c --- /dev/null +++ b/tdesign-component/example/shell/Icons/thumb_up.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/time.svg b/tdesign-component/example/shell/Icons/time.svg new file mode 100644 index 000000000..b4753d7b7 --- /dev/null +++ b/tdesign-component/example/shell/Icons/time.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/time_filled.svg b/tdesign-component/example/shell/Icons/time_filled.svg new file mode 100644 index 000000000..8653a8bd7 --- /dev/null +++ b/tdesign-component/example/shell/Icons/time_filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/tips.svg b/tdesign-component/example/shell/Icons/tips.svg new file mode 100644 index 000000000..51323cf82 --- /dev/null +++ b/tdesign-component/example/shell/Icons/tips.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/tools.svg b/tdesign-component/example/shell/Icons/tools.svg new file mode 100644 index 000000000..3da349f03 --- /dev/null +++ b/tdesign-component/example/shell/Icons/tools.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/unfold_less.svg b/tdesign-component/example/shell/Icons/unfold_less.svg new file mode 100644 index 000000000..5a07523e7 --- /dev/null +++ b/tdesign-component/example/shell/Icons/unfold_less.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/unfold_more.svg b/tdesign-component/example/shell/Icons/unfold_more.svg new file mode 100644 index 000000000..cfc8bd5aa --- /dev/null +++ b/tdesign-component/example/shell/Icons/unfold_more.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/upload.svg b/tdesign-component/example/shell/Icons/upload.svg new file mode 100644 index 000000000..c283ec03a --- /dev/null +++ b/tdesign-component/example/shell/Icons/upload.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/usb.svg b/tdesign-component/example/shell/Icons/usb.svg new file mode 100644 index 000000000..485a58253 --- /dev/null +++ b/tdesign-component/example/shell/Icons/usb.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/user.svg b/tdesign-component/example/shell/Icons/user.svg new file mode 100644 index 000000000..0dd37c805 --- /dev/null +++ b/tdesign-component/example/shell/Icons/user.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/user_add.svg b/tdesign-component/example/shell/Icons/user_add.svg new file mode 100644 index 000000000..ced38d530 --- /dev/null +++ b/tdesign-component/example/shell/Icons/user_add.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/user_avatar.svg b/tdesign-component/example/shell/Icons/user_avatar.svg new file mode 100644 index 000000000..b7c58da7c --- /dev/null +++ b/tdesign-component/example/shell/Icons/user_avatar.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/user_circle.svg b/tdesign-component/example/shell/Icons/user_circle.svg new file mode 100644 index 000000000..8e321e935 --- /dev/null +++ b/tdesign-component/example/shell/Icons/user_circle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/user_clear.svg b/tdesign-component/example/shell/Icons/user_clear.svg new file mode 100644 index 000000000..4fb77e220 --- /dev/null +++ b/tdesign-component/example/shell/Icons/user_clear.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/user_talk.svg b/tdesign-component/example/shell/Icons/user_talk.svg new file mode 100644 index 000000000..c78513c71 --- /dev/null +++ b/tdesign-component/example/shell/Icons/user_talk.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/usergroup.svg b/tdesign-component/example/shell/Icons/usergroup.svg new file mode 100644 index 000000000..cedfff73c --- /dev/null +++ b/tdesign-component/example/shell/Icons/usergroup.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/usergroup_add.svg b/tdesign-component/example/shell/Icons/usergroup_add.svg new file mode 100644 index 000000000..6bea98f0c --- /dev/null +++ b/tdesign-component/example/shell/Icons/usergroup_add.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tdesign-component/example/shell/Icons/usergroup_clear.svg b/tdesign-component/example/shell/Icons/usergroup_clear.svg new file mode 100644 index 000000000..3f69be642 --- /dev/null +++ b/tdesign-component/example/shell/Icons/usergroup_clear.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tdesign-component/example/shell/Icons/video.svg b/tdesign-component/example/shell/Icons/video.svg new file mode 100644 index 000000000..1e86e4ee2 --- /dev/null +++ b/tdesign-component/example/shell/Icons/video.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/view_column.svg b/tdesign-component/example/shell/Icons/view_column.svg new file mode 100644 index 000000000..1dbea27e9 --- /dev/null +++ b/tdesign-component/example/shell/Icons/view_column.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/view_list.svg b/tdesign-component/example/shell/Icons/view_list.svg new file mode 100644 index 000000000..09f59eb62 --- /dev/null +++ b/tdesign-component/example/shell/Icons/view_list.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/view_module.svg b/tdesign-component/example/shell/Icons/view_module.svg new file mode 100644 index 000000000..bca50898d --- /dev/null +++ b/tdesign-component/example/shell/Icons/view_module.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/tdesign-component/example/shell/Icons/wallet.svg b/tdesign-component/example/shell/Icons/wallet.svg new file mode 100644 index 000000000..9c9ff981a --- /dev/null +++ b/tdesign-component/example/shell/Icons/wallet.svg @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-component/example/shell/Icons/wifi.svg b/tdesign-component/example/shell/Icons/wifi.svg new file mode 100644 index 000000000..3f03fac47 --- /dev/null +++ b/tdesign-component/example/shell/Icons/wifi.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/tdesign-component/example/shell/Icons/zoom_in.svg b/tdesign-component/example/shell/Icons/zoom_in.svg new file mode 100644 index 000000000..4e29caff8 --- /dev/null +++ b/tdesign-component/example/shell/Icons/zoom_in.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/Icons/zoom_out.svg b/tdesign-component/example/shell/Icons/zoom_out.svg new file mode 100644 index 000000000..c78bae075 --- /dev/null +++ b/tdesign-component/example/shell/Icons/zoom_out.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tdesign-component/example/shell/build_web.sh b/tdesign-component/example/shell/build_web.sh new file mode 100644 index 000000000..51ae1644e --- /dev/null +++ b/tdesign-component/example/shell/build_web.sh @@ -0,0 +1,5 @@ +# 构建web应用 +cd .. +~/tools/flutter1/bin/flutter clean +~/tools/flutter1/bin/flutter pub get +~/tools/flutter1/bin/flutter build web --base-href /tdesign_example/ --source-maps \ No newline at end of file diff --git a/tdesign-component/example/shell/color_util/color.txt b/tdesign-component/example/shell/color_util/color.txt new file mode 100644 index 000000000..2ab1724e9 --- /dev/null +++ b/tdesign-component/example/shell/color_util/color.txt @@ -0,0 +1,4980 @@ +/* Color_Light Mode */ + +position: relative; +width: 1440px; +height: 2035px; + +/* Gray 中性/White */ +background: #FFFFFF; +border-radius: 24px; + + +/* Color_Light Mode */ + +position: absolute; +width: 306px; +height: 44px; +left: 96px; +top: 80px; + +/* Headline/Large */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 600; +font-size: 36px; +line-height: 44px; +/* identical to box height, or 122% */ + +/* Text&Icon/Font Gy1 90% */ +color: rgba(0, 0, 0, 0.9); + + + +/* Frame 1 */ + +position: absolute; +width: 1248px; +height: 557px; +left: 96px; +top: 188px; + + + +/* divider */ + +position: absolute; +width: 1248px; +height: 1px; +left: 0px; +top: 52px; + +/* Gray 中性/Gray3 */ +background: #E7E7E7; + + +/* 功能色 */ + +position: absolute; +width: 60px; +height: 28px; +left: 0px; +top: 0px; + +/* Title/ExtraLarge */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 600; +font-size: 20px; +line-height: 28px; +/* identical to box height, or 140% */ + +/* Text&Icon/Font Gy1 90% */ +color: rgba(0, 0, 0, 0.9); + + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 0px; +top: 77px; + +/* Brand 品牌/Brand1-Light */ +background: #F2F3FF; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 328px; +top: 77px; + +/* Error 错误/Error1-Light */ +background: #FFF0ED; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 656px; +top: 77px; + +/* Warning 警告/Warning1-Light */ +background: #FFF1E9; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 984px; +top: 77px; + +/* Success 成功/Success1-Light */ +background: #E3F9E9; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 0px; +top: 125px; + +/* Brand 品牌/Brand2-Focus */ +background: #D9E1FF; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 328px; +top: 125px; + +/* Error 错误/Error2-Focus */ +background: #FFD8D2; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 656px; +top: 125px; + +/* Warning 警告/Warning2-Focus */ +background: #FFD9C2; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 984px; +top: 125px; + +/* Success 成功/Success2-Focus */ +background: #C6F3D7; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 0px; +top: 173px; + +/* Brand 品牌/Brand3-Disabled */ +background: #B5C7FF; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 328px; +top: 173px; + +/* Error 错误/Error3-Disabled */ +background: #FFB9B0; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 656px; +top: 173px; + +/* Warning 警告/Warning3-Disabled */ +background: #FFB98C; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 984px; +top: 173px; + +/* Success 成功/Success3-Disabled */ +background: #92DAB2; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 0px; +top: 221px; + +/* Brand 品牌/Brand4 */ +background: #8EABFF; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 328px; +top: 221px; + +/* Error 错误/Error4 */ +background: #FF9285; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 656px; +top: 221px; + +/* Warning 警告/Warning4-Hover */ +background: #FA9550; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 984px; +top: 221px; + +/* Success 成功/Success4-Hover */ +background: #56C08D; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 0px; +top: 365px; + +/* Brand 品牌/Brand7-Normal */ +background: #0052D9; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 328px; +top: 365px; + +/* Error 错误/Error7-Click */ +background: #AD352F; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 656px; +top: 365px; + +/* Warning 警告/Warning7 */ +background: #954500; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 984px; +top: 365px; + +/* Success 成功/Success7 */ +background: #006C45; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 0px; +top: 269px; + +/* Brand 品牌/Brand5 */ +background: #618DFF; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 328px; +top: 269px; + +/* Error 错误/Error5-Hover */ +background: #F6685D; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 656px; +top: 269px; + +/* Warning 警告/Warning5-Normal */ +background: #E37318; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 984px; +top: 269px; + +/* Success 成功/Success5-Normal */ +background: #2BA471; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 0px; +top: 413px; + +/* Brand 品牌/Brand8-Click */ +background: #003CAB; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 328px; +top: 413px; + +/* Error 错误/Error8 */ +background: #881F1C; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 656px; +top: 413px; + +/* Warning 警告/Warning8 */ +background: #713300; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 984px; +top: 413px; + +/* Success 成功/Success8 */ +background: #005334; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 0px; +top: 317px; + +/* Brand 品牌/Brand6-Hover */ +background: #366EF4; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 328px; +top: 317px; + +/* Error 错误/Error6-Normal */ +background: #D54941; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 656px; +top: 317px; + +/* Warning 警告/Warning6-Click */ +background: #BE5A00; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 984px; +top: 317px; + +/* Success 成功/Success6-Click */ +background: #008858; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 0px; +top: 461px; + +/* Brand 品牌/Brand9 */ +background: #002A7C; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 328px; +top: 461px; + +/* Error 错误/Error9 */ +background: #68070A; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 656px; +top: 461px; + +/* Warning 警告/Warning9 */ +background: #532300; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 984px; +top: 461px; + +/* Success 成功/Success9 */ +background: #003B23; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 0px; +top: 509px; + +/* Brand 品牌/Brand10 */ +background: #001A57; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 328px; +top: 509px; + +/* Error 错误/Error10 */ +background: #490002; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 656px; +top: 509px; + +/* Warning 警告/Warning10 */ +background: #3B1700; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 984px; +top: 509px; + +/* Success 成功/Success10 */ +background: #002515; + + +/* Normal */ + +position: absolute; +width: 40px; +height: 20px; +left: 9px; +top: 389px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Normal */ + +position: absolute; +width: 40px; +height: 20px; +left: 336px; +top: 341px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Normal */ + +position: absolute; +width: 40px; +height: 20px; +left: 664px; +top: 293px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Normal */ + +position: absolute; +width: 40px; +height: 20px; +left: 992px; +top: 293px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Hover */ + +position: absolute; +width: 33px; +height: 20px; +left: 8px; +top: 341px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Hover */ + +position: absolute; +width: 33px; +height: 20px; +left: 336px; +top: 293px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Hover */ + +position: absolute; +width: 33px; +height: 20px; +left: 664px; +top: 245px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Hover */ + +position: absolute; +width: 33px; +height: 20px; +left: 992px; +top: 245px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Click */ + +position: absolute; +width: 28px; +height: 20px; +left: 8px; +top: 437px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Click */ + +position: absolute; +width: 28px; +height: 20px; +left: 336px; +top: 389px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Click */ + +position: absolute; +width: 28px; +height: 20px; +left: 664px; +top: 341px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Click */ + +position: absolute; +width: 28px; +height: 20px; +left: 992px; +top: 341px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Brand1 */ + +position: absolute; +width: 38px; +height: 20px; +left: 8px; +top: 81px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Error1 */ + +position: absolute; +width: 33px; +height: 20px; +left: 336px; +top: 81px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Light */ + +position: absolute; +width: 29px; +height: 20px; +left: 8px; +top: 101px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Warning1 */ + +position: absolute; +width: 51px; +height: 20px; +left: 664px; +top: 81px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Light */ + +position: absolute; +width: 29px; +height: 20px; +left: 664px; +top: 101px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Success1 */ + +position: absolute; +width: 52px; +height: 20px; +left: 992px; +top: 81px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #F2F3FF */ + +position: absolute; +width: 50px; +height: 20px; +left: 206px; +top: 81px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #FFF0ED */ + +position: absolute; +width: 52px; +height: 20px; +left: 532px; +top: 81px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #FFF1E9 */ + +position: absolute; +width: 48px; +height: 20px; +left: 864px; +top: 81px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #E3F9E9 */ + +position: absolute; +width: 51px; +height: 20px; +left: 1189px; +top: 81px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Brand3 */ + +position: absolute; +width: 41px; +height: 20px; +left: 8px; +top: 177px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Error3 */ + +position: absolute; +width: 36px; +height: 20px; +left: 336px; +top: 177px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Warning3 */ + +position: absolute; +width: 54px; +height: 20px; +left: 664px; +top: 177px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Success3 */ + +position: absolute; +width: 54px; +height: 20px; +left: 992px; +top: 177px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #B5C7FF */ + +position: absolute; +width: 52px; +height: 20px; +left: 204px; +top: 177px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #FFB9B0 */ + +position: absolute; +width: 52px; +height: 20px; +left: 532px; +top: 177px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #FFB98C */ + +position: absolute; +width: 53px; +height: 20px; +left: 859px; +top: 177px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #92DAB2 */ + +position: absolute; +width: 54px; +height: 20px; +left: 1186px; +top: 177px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Brand7 */ + +position: absolute; +width: 40px; +height: 20px; +left: 8px; +top: 369px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Error7 */ + +position: absolute; +width: 35px; +height: 20px; +left: 336px; +top: 369px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Warning7 */ + +position: absolute; +width: 53px; +height: 20px; +left: 664px; +top: 369px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Success7 */ + +position: absolute; +width: 53px; +height: 20px; +left: 992px; +top: 369px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #0052D9 */ + +position: absolute; +width: 52px; +height: 20px; +left: 204px; +top: 369px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #AD352F */ + +position: absolute; +width: 53px; +height: 20px; +left: 531px; +top: 369px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #954500 */ + +position: absolute; +width: 51px; +height: 20px; +left: 861px; +top: 369px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #006C45 */ + +position: absolute; +width: 52px; +height: 20px; +left: 1188px; +top: 369px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Brand5 */ + +position: absolute; +width: 41px; +height: 20px; +left: 8px; +top: 273px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Error5 */ + +position: absolute; +width: 36px; +height: 20px; +left: 336px; +top: 273px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Warning5 */ + +position: absolute; +width: 54px; +height: 20px; +left: 664px; +top: 273px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Success5 */ + +position: absolute; +width: 54px; +height: 20px; +left: 992px; +top: 273px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #618DFF */ + +position: absolute; +width: 49px; +height: 20px; +left: 207px; +top: 273px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #F6685D */ + +position: absolute; +width: 52px; +height: 20px; +left: 532px; +top: 273px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #E37318 */ + +position: absolute; +width: 48px; +height: 20px; +left: 864px; +top: 273px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #2BA471 */ + +position: absolute; +width: 49px; +height: 20px; +left: 1191px; +top: 273px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Brand9 */ + +position: absolute; +width: 41px; +height: 20px; +left: 8px; +top: 465px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Error9 */ + +position: absolute; +width: 36px; +height: 20px; +left: 336px; +top: 465px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Warning9 */ + +position: absolute; +width: 54px; +height: 20px; +left: 664px; +top: 465px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Success9 */ + +position: absolute; +width: 54px; +height: 20px; +left: 992px; +top: 465px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #002A7C */ + +position: absolute; +width: 52px; +height: 20px; +left: 204px; +top: 465px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #68070A */ + +position: absolute; +width: 51px; +height: 20px; +left: 533px; +top: 465px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #532300 */ + +position: absolute; +width: 51px; +height: 20px; +left: 861px; +top: 465px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #003B23 */ + +position: absolute; +width: 52px; +height: 20px; +left: 1188px; +top: 465px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Brand2 */ + +position: absolute; +width: 41px; +height: 20px; +left: 8px; +top: 129px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Error2 */ + +position: absolute; +width: 36px; +height: 20px; +left: 336px; +top: 129px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Warning2 */ + +position: absolute; +width: 54px; +height: 20px; +left: 664px; +top: 129px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Success2 */ + +position: absolute; +width: 54px; +height: 20px; +left: 992px; +top: 129px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #D9E1FF */ + +position: absolute; +width: 50px; +height: 20px; +left: 206px; +top: 129px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #FFD8D2 */ + +position: absolute; +width: 53px; +height: 20px; +left: 531px; +top: 129px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #FFD9C2 */ + +position: absolute; +width: 53px; +height: 20px; +left: 859px; +top: 129px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #C6F3D7 */ + +position: absolute; +width: 53px; +height: 20px; +left: 1187px; +top: 129px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Focus */ + +position: absolute; +width: 34px; +height: 20px; +left: 8px; +top: 149px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Disabled */ + +position: absolute; +width: 48px; +height: 20px; +left: 8px; +top: 197px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Light */ + +position: absolute; +width: 29px; +height: 20px; +left: 336px; +top: 101px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Focus */ + +position: absolute; +width: 34px; +height: 20px; +left: 336px; +top: 149px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Disabled */ + +position: absolute; +width: 48px; +height: 20px; +left: 336px; +top: 197px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Focus */ + +position: absolute; +width: 34px; +height: 20px; +left: 664px; +top: 149px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Disabled */ + +position: absolute; +width: 48px; +height: 20px; +left: 664px; +top: 197px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Focus */ + +position: absolute; +width: 34px; +height: 20px; +left: 992px; +top: 149px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Disabled */ + +position: absolute; +width: 48px; +height: 20px; +left: 992px; +top: 197px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Brand4 */ + +position: absolute; +width: 41px; +height: 20px; +left: 8px; +top: 225px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Error4 */ + +position: absolute; +width: 36px; +height: 20px; +left: 336px; +top: 225px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Warning4 */ + +position: absolute; +width: 54px; +height: 20px; +left: 664px; +top: 225px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Success4 */ + +position: absolute; +width: 54px; +height: 20px; +left: 992px; +top: 225px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #8EABFF */ + +position: absolute; +width: 52px; +height: 20px; +left: 204px; +top: 225px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #FF9285 */ + +position: absolute; +width: 50px; +height: 20px; +left: 534px; +top: 225px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #FA9550 */ + +position: absolute; +width: 51px; +height: 20px; +left: 861px; +top: 225px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #56C08D */ + +position: absolute; +width: 54px; +height: 20px; +left: 1186px; +top: 225px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Brand8 */ + +position: absolute; +width: 41px; +height: 20px; +left: 8px; +top: 417px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Error8 */ + +position: absolute; +width: 36px; +height: 20px; +left: 336px; +top: 417px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Warning8 */ + +position: absolute; +width: 54px; +height: 20px; +left: 664px; +top: 417px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Success8 */ + +position: absolute; +width: 54px; +height: 20px; +left: 992px; +top: 417px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #003CAB */ + +position: absolute; +width: 54px; +height: 20px; +left: 202px; +top: 417px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #881F1C */ + +position: absolute; +width: 47px; +height: 20px; +left: 537px; +top: 417px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #713300 */ + +position: absolute; +width: 48px; +height: 20px; +left: 864px; +top: 417px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #005334 */ + +position: absolute; +width: 51px; +height: 20px; +left: 1189px; +top: 417px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Brand6 */ + +position: absolute; +width: 41px; +height: 20px; +left: 8px; +top: 321px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Error6 */ + +position: absolute; +width: 36px; +height: 20px; +left: 336px; +top: 321px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Warning6 */ + +position: absolute; +width: 54px; +height: 20px; +left: 664px; +top: 321px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Success6 */ + +position: absolute; +width: 54px; +height: 20px; +left: 992px; +top: 321px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #366EF4 */ + +position: absolute; +width: 51px; +height: 20px; +left: 205px; +top: 321px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #D54941 */ + +position: absolute; +width: 50px; +height: 20px; +left: 534px; +top: 321px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #BE5A00 */ + +position: absolute; +width: 53px; +height: 20px; +left: 859px; +top: 321px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #008858 */ + +position: absolute; +width: 51px; +height: 20px; +left: 1189px; +top: 321px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Brand10 */ + +position: absolute; +width: 45px; +height: 20px; +left: 8px; +top: 513px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Error10 */ + +position: absolute; +width: 40px; +height: 20px; +left: 336px; +top: 513px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Warning10 */ + +position: absolute; +width: 58px; +height: 20px; +left: 664px; +top: 513px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Success10 */ + +position: absolute; +width: 59px; +height: 20px; +left: 992px; +top: 513px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #001A57 */ + +position: absolute; +width: 49px; +height: 20px; +left: 207px; +top: 513px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #490002 */ + +position: absolute; +width: 51px; +height: 20px; +left: 533px; +top: 513px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #3B1700 */ + +position: absolute; +width: 49px; +height: 20px; +left: 863px; +top: 513px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #002515 */ + +position: absolute; +width: 48px; +height: 20px; +left: 1192px; +top: 513px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Light */ + +position: absolute; +width: 29px; +height: 20px; +left: 992px; +top: 101px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* divider */ + +position: absolute; +width: 1248px; +height: 1px; +left: 96px; +top: 861px; + +/* Gray 中性/Gray3 */ +background: #E7E7E7; + + +/* divider */ + +position: absolute; +width: 1248px; +height: 1px; +left: 96px; +top: 1194px; + +/* Gray 中性/Gray3 */ +background: #E7E7E7; + + +/* 文字&图标色 */ + +position: absolute; +width: 116px; +height: 28px; +left: 96px; +top: 809px; + +/* Title/ExtraLarge */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 600; +font-size: 20px; +line-height: 28px; +/* identical to box height, or 140% */ + +/* Text&Icon/Font Gy1 90% */ +color: rgba(0, 0, 0, 0.9); + + + +/* 中性色板 */ + +position: absolute; +width: 80px; +height: 28px; +left: 96px; +top: 1142px; + +/* Title/ExtraLarge */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 600; +font-size: 20px; +line-height: 28px; +/* identical to box height, or 140% */ + +/* Text&Icon/Font Gy1 90% */ +color: rgba(0, 0, 0, 0.9); + + + +/* Rectangle 59 */ + +position: absolute; +width: 264px; +height: 192px; +left: 424px; +top: 886px; + +/* Text&Icon/Font Gy1 90% */ +background: rgba(0, 0, 0, 0.9); + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 886px; + +/* Text&Icon/Font Gy4 26% */ +background: rgba(0, 0, 0, 0.26); + + +/* Example */ + +position: absolute; +width: 248px; +height: 44px; +left: 432px; +top: 938px; + +/* Text&Icon/Font Wh3 35% */ +background: rgba(255, 255, 255, 0.35); + + +/* Example */ + +position: absolute; +width: 248px; +height: 44px; +left: 432px; +top: 894px; + +/* Text&Icon/Font Wh4 22% */ +background: rgba(255, 255, 255, 0.22); + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 934px; + +/* Text&Icon/Font Gy3 40% */ +background: rgba(0, 0, 0, 0.4); + + +/* Example */ + +position: absolute; +width: 248px; +height: 44px; +left: 432px; +top: 982px; + +/* Text&Icon/Font Wh2 55% */ +background: rgba(255, 255, 255, 0.55); + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 982px; + +/* Text&Icon/Font Gy2 60% */ +background: rgba(0, 0, 0, 0.6); + + +/* Example */ + +position: absolute; +width: 248px; +height: 44px; +left: 432px; +top: 1026px; + +/* Text&Icon/Font Wh1 100% */ +background: #FFFFFF; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1030px; + +/* Text&Icon/Font Gy1 90% */ +background: rgba(0, 0, 0, 0.9); + + +/* Font Gy4 */ + +position: absolute; +width: 52px; +height: 20px; +left: 104px; +top: 890px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Font Wh3 */ + +position: absolute; +width: 54px; +height: 20px; +left: 440px; +top: 940px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Font Wh4 */ + +position: absolute; +width: 54px; +height: 20px; +left: 440px; +top: 896px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #000000 26% */ + +position: absolute; +width: 81px; +height: 20px; +left: 271px; +top: 890px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #FFFFFF 35% */ + +position: absolute; +width: 79px; +height: 20px; +left: 593px; +top: 940px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #FFFFFF 22% */ + +position: absolute; +width: 79px; +height: 20px; +left: 593px; +top: 896px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Font Gy2 */ + +position: absolute; +width: 52px; +height: 20px; +left: 104px; +top: 986px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Font Wh1 */ + +position: absolute; +width: 52px; +height: 20px; +left: 440px; +top: 1028px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy1 90% */ +color: rgba(0, 0, 0, 0.9); + + + +/* #000000 60% */ + +position: absolute; +width: 81px; +height: 20px; +left: 271px; +top: 986px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #FFFFFF 100% */ + +position: absolute; +width: 84px; +height: 20px; +left: 588px; +top: 1028px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy1 90% */ +color: rgba(0, 0, 0, 0.9); + + + +/* Font Gy3 */ + +position: absolute; +width: 52px; +height: 20px; +left: 104px; +top: 938px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Font Wh2 */ + +position: absolute; +width: 54px; +height: 20px; +left: 440px; +top: 984px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #000000 40% */ + +position: absolute; +width: 81px; +height: 20px; +left: 271px; +top: 938px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #FFFFFF 55% */ + +position: absolute; +width: 79px; +height: 20px; +left: 593px; +top: 984px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Font Gy1 */ + +position: absolute; +width: 49px; +height: 20px; +left: 104px; +top: 1034px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #000000 90% */ + +position: absolute; +width: 81px; +height: 20px; +left: 271px; +top: 1034px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1267px; + +/* Gray 中性/Gray1 */ +background: #F3F3F3; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1267px; + +/* Gray 中性/Blue Gy1 */ +background: #F3F3F4; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1219px; + +/* Gray 中性/White */ +background: #FFFFFF; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1219px; + +/* Gray 中性/White */ +background: #FFFFFF; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1315px; + +/* Gray 中性/Gray2 */ +background: #EEEEEE; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1315px; + +/* Gray 中性/Blue Gy2 */ +background: #EEEEF0; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1363px; + +/* Gray 中性/Gray3 */ +background: #E7E7E7; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1363px; + +/* Gray 中性/Blue Gy3 */ +background: #E7E8EB; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1411px; + +/* Gray 中性/Gray4 */ +background: #DCDCDC; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1411px; + +/* Gray 中性/Blue Gy4 */ +background: #DCDDE1; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1555px; + +/* Gray 中性/Gray7 */ +background: #8B8B8B; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1555px; + +/* Gray 中性/Blue Gy7 */ +background: #858A99; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1459px; + +/* Gray 中性/Gray5 */ +background: #C5C5C5; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1459px; + +/* Gray 中性/Blue Gy5 */ +background: #C4C6CD; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1603px; + +/* Gray 中性/Gray8 */ +background: #777777; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1603px; + +/* Gray 中性/Blue Gy8 */ +background: #6F7686; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1507px; + +/* Gray 中性/Gray6 */ +background: #A6A6A6; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1507px; + +/* Gray 中性/Blue Gy6 */ +background: #A2A6B1; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1651px; + +/* Gray 中性/Gray9 */ +background: #5E5E5E; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1651px; + +/* Gray 中性/Blue Gy9 */ +background: #535D6D; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1699px; + +/* Gray 中性/Gray10 */ +background: #4B4B4B; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1699px; + +/* Gray 中性/Blue Gy10 */ +background: #424A57; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1747px; + +/* Gray 中性/Gray11 */ +background: #383838; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1747px; + +/* Gray 中性/Blue Gy11 */ +background: #323843; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1795px; + +/* Gray 中性/Gray12 */ +background: #2C2C2C; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1795px; + +/* Gray 中性/Blue Gy12 */ +background: #272B34; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1843px; + +/* Gray 中性/Gray13 */ +background: #242424; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1843px; + +/* Gray 中性/Blue Gy13 */ +background: #20232B; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1891px; + +/* Gray 中性/Gray14 */ +background: #181818; + + +/* Example */ + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1891px; + +/* Gray 中性/Blue Gy14 */ +background: #15181D; + + +/* Gray1 */ + +position: absolute; +width: 31px; +height: 20px; +left: 104px; +top: 1271px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Gray1 */ + +position: absolute; +width: 31px; +height: 20px; +left: 432px; +top: 1271px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* White1 */ + +position: absolute; +width: 37px; +height: 20px; +left: 104px; +top: 1223px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* White1 */ + +position: absolute; +width: 37px; +height: 20px; +left: 432px; +top: 1223px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #F3F3F3 */ + +position: absolute; +width: 50px; +height: 20px; +left: 302px; +top: 1271px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #F3F3F4 */ + +position: absolute; +width: 50px; +height: 20px; +left: 630px; +top: 1271px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #FFFFFF */ + +position: absolute; +width: 49px; +height: 20px; +left: 303px; +top: 1223px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #FFFFFF */ + +position: absolute; +width: 49px; +height: 20px; +left: 631px; +top: 1223px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Gray3 */ + +position: absolute; +width: 34px; +height: 20px; +left: 104px; +top: 1367px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Gray3 */ + +position: absolute; +width: 34px; +height: 20px; +left: 432px; +top: 1367px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #E7E7E7 */ + +position: absolute; +width: 50px; +height: 20px; +left: 302px; +top: 1367px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #E7E8EB */ + +position: absolute; +width: 52px; +height: 20px; +left: 628px; +top: 1367px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Gray7 */ + +position: absolute; +width: 33px; +height: 20px; +left: 104px; +top: 1559px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray7 */ + +position: absolute; +width: 33px; +height: 20px; +left: 432px; +top: 1559px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #8B8B8B */ + +position: absolute; +width: 54px; +height: 20px; +left: 298px; +top: 1559px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #858A99 */ + +position: absolute; +width: 52px; +height: 20px; +left: 628px; +top: 1559px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray5 */ + +position: absolute; +width: 34px; +height: 20px; +left: 104px; +top: 1463px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Gray5 */ + +position: absolute; +width: 34px; +height: 20px; +left: 432px; +top: 1463px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #C5C5C5 */ + +position: absolute; +width: 55px; +height: 20px; +left: 297px; +top: 1463px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #C4C6CD */ + +position: absolute; +width: 57px; +height: 20px; +left: 623px; +top: 1463px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Gray9 */ + +position: absolute; +width: 34px; +height: 20px; +left: 104px; +top: 1655px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray9 */ + +position: absolute; +width: 34px; +height: 20px; +left: 432px; +top: 1655px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #5E5E5E */ + +position: absolute; +width: 52px; +height: 20px; +left: 300px; +top: 1655px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #535D6D */ + +position: absolute; +width: 53px; +height: 20px; +left: 627px; +top: 1655px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray2 */ + +position: absolute; +width: 34px; +height: 20px; +left: 104px; +top: 1319px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Gray2 */ + +position: absolute; +width: 34px; +height: 20px; +left: 432px; +top: 1319px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #EEEEEE */ + +position: absolute; +width: 54px; +height: 20px; +left: 298px; +top: 1319px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #EEEEF0 */ + +position: absolute; +width: 52px; +height: 20px; +left: 628px; +top: 1319px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Gray4 */ + +position: absolute; +width: 34px; +height: 20px; +left: 104px; +top: 1415px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Gray4 */ + +position: absolute; +width: 34px; +height: 20px; +left: 432px; +top: 1415px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #DCDCDC */ + +position: absolute; +width: 59px; +height: 20px; +left: 293px; +top: 1415px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* #DCDDE1 */ + +position: absolute; +width: 54px; +height: 20px; +left: 626px; +top: 1415px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Gy2 60% */ +color: rgba(0, 0, 0, 0.6); + + + +/* Gray8 */ + +position: absolute; +width: 34px; +height: 20px; +left: 104px; +top: 1607px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray8 */ + +position: absolute; +width: 34px; +height: 20px; +left: 432px; +top: 1607px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #777777 */ + +position: absolute; +width: 47px; +height: 20px; +left: 305px; +top: 1607px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #6F7686 */ + +position: absolute; +width: 50px; +height: 20px; +left: 630px; +top: 1607px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray6 */ + +position: absolute; +width: 34px; +height: 20px; +left: 104px; +top: 1511px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray6 */ + +position: absolute; +width: 34px; +height: 20px; +left: 432px; +top: 1511px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #A6A6A6 */ + +position: absolute; +width: 53px; +height: 20px; +left: 299px; +top: 1511px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #A2A6B1 */ + +position: absolute; +width: 51px; +height: 20px; +left: 629px; +top: 1511px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray10 */ + +position: absolute; +width: 39px; +height: 20px; +left: 104px; +top: 1703px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray10 */ + +position: absolute; +width: 39px; +height: 20px; +left: 432px; +top: 1703px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray11 */ + +position: absolute; +width: 36px; +height: 20px; +left: 104px; +top: 1751px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray11 */ + +position: absolute; +width: 36px; +height: 20px; +left: 432px; +top: 1751px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray12 */ + +position: absolute; +width: 39px; +height: 20px; +left: 104px; +top: 1799px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray12 */ + +position: absolute; +width: 39px; +height: 20px; +left: 432px; +top: 1799px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray13 */ + +position: absolute; +width: 39px; +height: 20px; +left: 104px; +top: 1847px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray13 */ + +position: absolute; +width: 39px; +height: 20px; +left: 432px; +top: 1847px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray14 */ + +position: absolute; +width: 39px; +height: 20px; +left: 104px; +top: 1895px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Gray14 */ + +position: absolute; +width: 39px; +height: 20px; +left: 432px; +top: 1895px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #4B4B4B */ + +position: absolute; +width: 54px; +height: 20px; +left: 298px; +top: 1703px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #424A57 */ + +position: absolute; +width: 51px; +height: 20px; +left: 629px; +top: 1703px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #383838 */ + +position: absolute; +width: 51px; +height: 20px; +left: 301px; +top: 1751px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #323843 */ + +position: absolute; +width: 51px; +height: 20px; +left: 629px; +top: 1751px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #2C2C2C */ + +position: absolute; +width: 55px; +height: 20px; +left: 297px; +top: 1799px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #272B34 */ + +position: absolute; +width: 51px; +height: 20px; +left: 629px; +top: 1799px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #242424 */ + +position: absolute; +width: 51px; +height: 20px; +left: 301px; +top: 1847px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #20232B */ + +position: absolute; +width: 52px; +height: 20px; +left: 628px; +top: 1847px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #181818 */ + +position: absolute; +width: 44px; +height: 20px; +left: 308px; +top: 1895px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* #15181D */ + +position: absolute; +width: 45px; +height: 20px; +left: 635px; +top: 1895px; + +/* Body/Small */ +font-family: 'PingFang SC'; +font-style: normal; +font-weight: 400; +font-size: 12px; +line-height: 20px; +/* identical to box height, or 167% */ +text-align: right; + +/* Text&Icon/Font Wh1 100% */ +color: #FFFFFF; + + + +/* Rectangle 57 */ + +box-sizing: border-box; + +position: absolute; +width: 264px; +height: 48px; +left: 96px; +top: 1219px; + +border: 1px solid #DCDCDC; + + +/* Rectangle 58 */ + +box-sizing: border-box; + +position: absolute; +width: 264px; +height: 48px; +left: 424px; +top: 1219px; + +/* Gray 中性/Gray4 */ +border: 1px solid #DCDCDC; diff --git a/tdesign-component/example/shell/flutter_versions/3.16.9/copy.sh b/tdesign-component/example/shell/flutter_versions/3.16.9/copy.sh new file mode 100644 index 000000000..2f0cd896d --- /dev/null +++ b/tdesign-component/example/shell/flutter_versions/3.16.9/copy.sh @@ -0,0 +1,2 @@ +cp ./pubspec_overrides.yaml ../../../../ +cp ./pubspec_overrides.yaml ../../../ \ No newline at end of file diff --git a/tdesign-component/example/shell/flutter_versions/3.16.9/pubspec_overrides.yaml b/tdesign-component/example/shell/flutter_versions/3.16.9/pubspec_overrides.yaml new file mode 100644 index 000000000..21bf2d406 --- /dev/null +++ b/tdesign-component/example/shell/flutter_versions/3.16.9/pubspec_overrides.yaml @@ -0,0 +1,3 @@ +dependency_overrides: + tdesign_flutter_adaptation: 3.16.0 + image_picker: 1.0.8 \ No newline at end of file diff --git a/tdesign-component/example/shell/flutter_versions/3.32.2/copy.sh b/tdesign-component/example/shell/flutter_versions/3.32.2/copy.sh new file mode 100644 index 000000000..2f0cd896d --- /dev/null +++ b/tdesign-component/example/shell/flutter_versions/3.32.2/copy.sh @@ -0,0 +1,2 @@ +cp ./pubspec_overrides.yaml ../../../../ +cp ./pubspec_overrides.yaml ../../../ \ No newline at end of file diff --git a/tdesign-component/example/shell/flutter_versions/3.32.2/pubspec_overrides.yaml b/tdesign-component/example/shell/flutter_versions/3.32.2/pubspec_overrides.yaml new file mode 100644 index 000000000..584af4598 --- /dev/null +++ b/tdesign-component/example/shell/flutter_versions/3.32.2/pubspec_overrides.yaml @@ -0,0 +1,2 @@ +dependency_overrides: + tdesign_flutter_adaptation: 3.32.0 \ No newline at end of file diff --git a/tdesign-component/example/shell/script.py b/tdesign-component/example/shell/script.py new file mode 100644 index 000000000..0542da023 --- /dev/null +++ b/tdesign-component/example/shell/script.py @@ -0,0 +1,76 @@ +# script.py +import os +from openai import OpenAI +import json + +# 获取事件数据文件路径 +event_path = os.getenv('GITHUB_EVENT_PATH') + +if not event_path: + raise ValueError("GITHUB_EVENT_PATH 环境变量未找到") + +# 读取并解析 JSON 数据 +with open(event_path, 'r') as f: + event_data = json.load(f) + +# 提取评论内容 +comment_body = event_data.get('comment', {}).get('body') + + +# Non-streaming: +# print("----- standard event_data:",event_data) +# print("----- standard comment_body:",comment_body) +print("----- standard request, comment_body:",comment_body) +# gets API Key from environment variable OPENAI_API_KEY +client = OpenAI( + api_key=os.getenv('HUNYUAN_API_KEY'), # 混元 APIKey + base_url="https://api.hunyuan.cloud.tencent.com/v1", # 混元 endpoint +) + +completion = client.chat.completions.create( + model="hunyuan-turbo", + messages=[ + { + "role": "user", + "content": f"""请帮我把以下中文文档翻译成英文,并以markdown格式输出: +{comment_body}""", + }, + ], +) +print(completion.choices[0].message.content) + +# 写入文件 +file_path = "../../CHANGELOG.md" +new_content = f"""{completion.choices[0].message.content} + + +""" +line_number = 1 + +try: + # 读取原文件内容 + with open(file_path, 'r', encoding='utf-8') as f: + lines = f.readlines() + + # 处理行号(将人类理解的 1-based 转为 0-based) + insert_pos = line_number - 1 + + # 处理行号超出范围的情况(插入到文件末尾) + if insert_pos > len(lines): + insert_pos = len(lines) + + # 插入新内容(自动添加换行符) + if not new_content.endswith('\n'): + new_content += '\n' + lines.insert(insert_pos, new_content) + + # 写回文件 + with open(file_path, 'w', encoding='utf-8') as f: + f.writelines(lines) + + print(f"成功在第 {line_number} 行插入内容") + +except FileNotFoundError: + print(f"错误:文件 {file_path} 不存在") +except Exception as e: + print(f"操作失败:{str(e)}") \ No newline at end of file diff --git a/tdesign-component/example/shell/svg2ttf.sh b/tdesign-component/example/shell/svg2ttf.sh new file mode 100644 index 000000000..b5644e033 --- /dev/null +++ b/tdesign-component/example/shell/svg2ttf.sh @@ -0,0 +1,13 @@ +# 首次运行时放开,替换成自己的flutter路径,建议以flutter 2.2.0允许,该库未适配flutter 3.+ +#flutterPath="~/tools/flutter" +#export PATH="$PATH":"$flutterPath/bin/" +#export PATH="$PATH":"~/tools/flutter/bin/cache/dart-sdk/bin/" +#export PATH="$PATH":"~/.pub-cache/bin" +#export PATH="$PATH":"~/.pub-cache/bin/icon_font_generator" +# +#flutter pub global activate icon_font_generator + +#----- 以上注释,首次允许时放开 -------- + +# svg放到icon目录(建议figma全量导出替换),运行此命令,生成ttf +~/.pub-cache/bin/icon_font_generator --from=icons --class-name=TDIcons --out-font=../../assets/tdesign/td_icons.ttf --out-flutter=../../lib/src/components/icon/td_icons.dart \ No newline at end of file diff --git a/tdesign-component/example/shell/td_icons/index.json b/tdesign-component/example/shell/td_icons/index.json new file mode 100644 index 000000000..69a39fbb3 --- /dev/null +++ b/tdesign-component/example/shell/td_icons/index.json @@ -0,0 +1,244 @@ +{"iconName":"t","icons":[{"name": "add-circle","svgCode": "\n\n\n\n","codepoint": "\\E001"}, +{"name": "add-rectangle","svgCode": "\n\n\n\n","codepoint": "\\E002"}, +{"name": "add","svgCode": "\n\n\n","codepoint": "\\E003"}, +{"name": "app","svgCode": "\n\n\n\n\n\n","codepoint": "\\E004"}, +{"name": "arrow-down-rectangle","svgCode": "\n\n\n\n","codepoint": "\\E005"}, +{"name": "arrow-down","svgCode": "\n\n\n","codepoint": "\\E006"}, +{"name": "arrow-left","svgCode": "\n\n\n","codepoint": "\\E007"}, +{"name": "arrow-right","svgCode": "\n\n\n","codepoint": "\\E008"}, +{"name": "arrow-triangle-down-filled","svgCode": "\n\n\n","codepoint": "\\E009"}, +{"name": "arrow-triangle-down","svgCode": "\n\n\n","codepoint": "\\E00A"}, +{"name": "arrow-triangle-up-filled","svgCode": "\n\n\n","codepoint": "\\E00B"}, +{"name": "arrow-triangle-up","svgCode": "\n\n\n","codepoint": "\\E00C"}, +{"name": "arrow-up","svgCode": "\n\n\n","codepoint": "\\E00D"}, +{"name": "attach","svgCode": "\n\n\n","codepoint": "\\E00E"}, +{"name": "backtop-rectangle","svgCode": "\n\n\n\n\n","codepoint": "\\E00F"}, +{"name": "backtop","svgCode": "\n\n\n\n","codepoint": "\\E010"}, +{"name": "backward","svgCode": "\n\n\n","codepoint": "\\E011"}, +{"name": "barcode","svgCode": "\n\n\n","codepoint": "\\E012"}, +{"name": "books","svgCode": "\n\n\n","codepoint": "\\E013"}, +{"name": "browse-off","svgCode": "\n\n\n\n\n","codepoint": "\\E014"}, +{"name": "browse","svgCode": "\n\n\n\n","codepoint": "\\E015"}, +{"name": "bulletpoint","svgCode": "\n\n\n\n\n\n\n\n","codepoint": "\\E016"}, +{"name": "calendar","svgCode": "\n\n\n","codepoint": "\\E017"}, +{"name": "call","svgCode": "\n\n\n","codepoint": "\\E018"}, +{"name": "caret-down-small","svgCode": "\n\n\n","codepoint": "\\E019"}, +{"name": "caret-down","svgCode": "\n\n\n","codepoint": "\\E01A"}, +{"name": "caret-left-small","svgCode": "\n\n\n","codepoint": "\\E01B"}, +{"name": "caret-left","svgCode": "\n\n\n","codepoint": "\\E01C"}, +{"name": "caret-right-small","svgCode": "\n\n\n","codepoint": "\\E01D"}, +{"name": "caret-right","svgCode": "\n\n\n","codepoint": "\\E01E"}, +{"name": "caret-up-small","svgCode": "\n\n\n","codepoint": "\\E01F"}, +{"name": "caret-up","svgCode": "\n\n\n","codepoint": "\\E020"}, +{"name": "cart","svgCode": "\n\n\n\n\n","codepoint": "\\E021"}, +{"name": "chart-bar","svgCode": "\n\n\n\n\n","codepoint": "\\E022"}, +{"name": "chart-bubble","svgCode": "\n\n\n\n\n\n","codepoint": "\\E023"}, +{"name": "chart-pie","svgCode": "\n\n\n","codepoint": "\\E024"}, +{"name": "chart","svgCode": "\n\n\n\n\n\n","codepoint": "\\E025"}, +{"name": "chat","svgCode": "\n\n\n","codepoint": "\\E026"}, +{"name": "check-circle-filled","svgCode": "\n\n\n","codepoint": "\\E027"}, +{"name": "check-circle","svgCode": "\n\n\n\n","codepoint": "\\E028"}, +{"name": "check-rectangle-filled","svgCode": "\n\n\n","codepoint": "\\E029"}, +{"name": "check-rectangle","svgCode": "\n\n\n\n","codepoint": "\\E02A"}, +{"name": "check","svgCode": "\n\n\n","codepoint": "\\E02B"}, +{"name": "chevron-down-circle","svgCode": "\n\n\n\n","codepoint": "\\E02C"}, +{"name": "chevron-down-rectangle","svgCode": "\n\n\n\n","codepoint": "\\E02D"}, +{"name": "chevron-down","svgCode": "\n\n\n","codepoint": "\\E02E"}, +{"name": "chevron-left-circle","svgCode": "\n\n\n\n","codepoint": "\\E02F"}, +{"name": "chevron-left-double","svgCode": "\n\n\n\n","codepoint": "\\E030"}, +{"name": "chevron-left-rectangle","svgCode": "\n\n\n\n","codepoint": "\\E031"}, +{"name": "chevron-left","svgCode": "\n\n\n","codepoint": "\\E032"}, +{"name": "chevron-right-circle","svgCode": "\n\n\n\n","codepoint": "\\E033"}, +{"name": "chevron-right-double","svgCode": "\n\n\n","codepoint": "\\E034"}, +{"name": "chevron-right-rectangle","svgCode": "\n\n\n\n","codepoint": "\\E035"}, +{"name": "chevron-right","svgCode": "\n\n\n","codepoint": "\\E036"}, +{"name": "chevron-up-circle","svgCode": "\n\n\n\n","codepoint": "\\E037"}, +{"name": "chevron-up-rectangle","svgCode": "\n\n\n\n","codepoint": "\\E038"}, +{"name": "chevron-up","svgCode": "\n\n\n","codepoint": "\\E039"}, +{"name": "circle","svgCode": "\n\n\n\n\n","codepoint": "\\E03A"}, +{"name": "clear","svgCode": "\n\n\n","codepoint": "\\E03B"}, +{"name": "close-circle-filled","svgCode": "\n\n\n","codepoint": "\\E03C"}, +{"name": "close-circle","svgCode": "\n\n\n\n","codepoint": "\\E03D"}, +{"name": "close-rectangle","svgCode": "\n\n\n\n","codepoint": "\\E03E"}, +{"name": "close","svgCode": "\n\n\n","codepoint": "\\E03F"}, +{"name": "cloud-download","svgCode": "\n\n\n\n","codepoint": "\\E040"}, +{"name": "cloud-upload","svgCode": "\n\n\n\n","codepoint": "\\E041"}, +{"name": "cloud","svgCode": "\n\n\n","codepoint": "\\E042"}, +{"name": "code","svgCode": "\n\n\n\n\n","codepoint": "\\E043"}, +{"name": "control-platform","svgCode": "\n\n\n","codepoint": "\\E044"}, +{"name": "creditcard","svgCode": "\n\n\n\n","codepoint": "\\E045"}, +{"name": "dashboard","svgCode": "\n\n\n\n\n\n\n","codepoint": "\\E046"}, +{"name": "delete","svgCode": "\n\n\n\n\n","codepoint": "\\E047"}, +{"name": "desktop","svgCode": "\n\n\n","codepoint": "\\E048"}, +{"name": "discount-filled","svgCode": "\n\n\n\n","codepoint": "\\E049"}, +{"name": "discount","svgCode": "\n \n \n\n ","codepoint": "\\E04A"}, +{"name": "download","svgCode": "\n\n\n\n","codepoint": "\\E04B"}, +{"name": "edit-1","svgCode": "\n\n\n\n\n\n\n\n","codepoint": "\\E04C"}, +{"name": "edit","svgCode": "\n\n\n\n","codepoint": "\\E04D"}, +{"name": "ellipsis","svgCode": "\n\n\n\n\n","codepoint": "\\E04E"}, +{"name": "enter","svgCode": "\n\n\n","codepoint": "\\E04F"}, +{"name": "error-circle-filled","svgCode": "\n\n\n","codepoint": "\\E050"}, +{"name": "error-circle","svgCode": "\n\n\n\n\n","codepoint": "\\E051"}, +{"name": "error","svgCode": "\n\n\n","codepoint": "\\E052"}, +{"name": "file-add","svgCode": "\n\n\n\n","codepoint": "\\E053"}, +{"name": "file-copy","svgCode": "\n\n\n\n","codepoint": "\\E054"}, +{"name": "file-excel","svgCode": "\n\n\n\n","codepoint": "\\E055"}, +{"name": "file-icon","svgCode": "\n\n\n\n\n\n\n","codepoint": "\\E056"}, +{"name": "file-image","svgCode": "\n\n\n","codepoint": "\\E057"}, +{"name": "file-paste","svgCode": "\n\n\n\n","codepoint": "\\E058"}, +{"name": "file-pdf","svgCode": "\n\n\n\n\n\n","codepoint": "\\E059"}, +{"name": "file-powerpoint","svgCode": "\n\n\n\n","codepoint": "\\E05A"}, +{"name": "file-unknown","svgCode": "\n\n\n\n\n","codepoint": "\\E05B"}, +{"name": "file-word","svgCode": "\n\n\n\n","codepoint": "\\E05C"}, +{"name": "file","svgCode": "\n\n\n","codepoint": "\\E05D"}, +{"name": "filter-clear","svgCode": "\n\n\n\n","codepoint": "\\E05E"}, +{"name": "filter","svgCode": "\n\n\n","codepoint": "\\E05F"}, +{"name": "flag","svgCode": "\n \n\n ","codepoint": "\\E060"}, +{"name": "folder-add","svgCode": "\n\n\n\n","codepoint": "\\E061"}, +{"name": "folder-open","svgCode": "\n\n\n\n","codepoint": "\\E062"}, +{"name": "folder","svgCode": "\n\n\n","codepoint": "\\E063"}, +{"name": "fork","svgCode": "\n\n\n","codepoint": "\\E064"}, +{"name": "format-horizontal-align-bottom","svgCode": "\n\n\n\n\n\n","codepoint": "\\E065"}, +{"name": "format-horizontal-align-center","svgCode": "\n\n\n\n\n\n","codepoint": "\\E066"}, +{"name": "format-horizontal-align-top","svgCode": "\n\n\n\n\n\n","codepoint": "\\E067"}, +{"name": "format-vertical-align-center","svgCode": "\n\n\n\n\n\n","codepoint": "\\E068"}, +{"name": "format-vertical-align-left","svgCode": "\n\n\n\n\n\n","codepoint": "\\E069"}, +{"name": "format-vertical-align-right","svgCode": "\n\n\n","codepoint": "\\E06A"}, +{"name": "forward","svgCode": "\n\n\n","codepoint": "\\E06B"}, +{"name": "fullscreen-exit","svgCode": "\n\n\n\n","codepoint": "\\E06C"}, +{"name": "fullscreen","svgCode": "\n\n\n\n","codepoint": "\\E06D"}, +{"name": "gender-female","svgCode": "\n\n\n","codepoint": "\\E06E"}, +{"name": "gender-male","svgCode": "\n\n\n","codepoint": "\\E06F"}, +{"name": "gift","svgCode": "\n\n\n","codepoint": "\\E070"}, +{"name": "heart-filled","svgCode": "\n\n\n","codepoint": "\\E071"}, +{"name": "heart","svgCode": "\n\n\n","codepoint": "\\E072"}, +{"name": "help-circle-filled","svgCode": "\n\n\n","codepoint": "\\E073"}, +{"name": "help-circle","svgCode": "\n\n\n\n\n","codepoint": "\\E074"}, +{"name": "help","svgCode": "\n\n\n\n","codepoint": "\\E075"}, +{"name": "history","svgCode": "\n\n\n\n","codepoint": "\\E076"}, +{"name": "home","svgCode": "\n\n\n\n","codepoint": "\\E077"}, +{"name": "hourglass","svgCode": "\n\n\n","codepoint": "\\E078"}, +{"name": "image-error","svgCode": "\n\n\n\n\n","codepoint": "\\E079"}, +{"name": "image","svgCode": "\n\n\n\n","codepoint": "\\E07A"}, +{"name": "info-circle-filled","svgCode": "\n\n\n","codepoint": "\\E07B"}, +{"name": "info-circle","svgCode": "\n\n\n\n\n","codepoint": "\\E07C"}, +{"name": "internet","svgCode": "\n\n\n","codepoint": "\\E07D"}, +{"name": "jump","svgCode": "\n\n\n\n","codepoint": "\\E07E"}, +{"name": "laptop","svgCode": "\n\n\n\n","codepoint": "\\E07F"}, +{"name": "layers","svgCode": "\n\n\n\n\n","codepoint": "\\E080"}, +{"name": "link-unlink","svgCode": "\n\n\n\n\n\n\n\n\n\n","codepoint": "\\E081"}, +{"name": "link","svgCode": "\n\n\n\n\n","codepoint": "\\E082"}, +{"name": "loading","svgCode": "\n\n\n","codepoint": "\\E083"}, +{"name": "location","svgCode": "\n\n\n\n","codepoint": "\\E084"}, +{"name": "lock-off","svgCode": "\n\n\n\n","codepoint": "\\E085"}, +{"name": "lock-on","svgCode": "\n\n\n\n","codepoint": "\\E086"}, +{"name": "login","svgCode": "\n\n\n\n","codepoint": "\\E087"}, +{"name": "logo-android","svgCode": "\n\n\n\n\n","codepoint": "\\E088"}, +{"name": "logo-apple-filled","svgCode": "\n\n\n\n","codepoint": "\\E089"}, +{"name": "logo-apple","svgCode": "\n\n\n\n","codepoint": "\\E08A"}, +{"name": "logo-chrome-filled","svgCode": "\n\n\n\n\n\n","codepoint": "\\E08B"}, +{"name": "logo-chrome","svgCode": "\n\n\n","codepoint": "\\E08C"}, +{"name": "logo-codepen","svgCode": "\n\n\n","codepoint": "\\E08D"}, +{"name": "logo-github-filled","svgCode": "\n\n\n","codepoint": "\\E08E"}, +{"name": "logo-github","svgCode": "\n\n\n","codepoint": "\\E08F"}, +{"name": "logo-ie-filled","svgCode": "\n\n\n","codepoint": "\\E090"}, +{"name": "logo-ie","svgCode": "\n\n\n\n","codepoint": "\\E091"}, +{"name": "logo-qq","svgCode": "\n\n\n","codepoint": "\\E092"}, +{"name": "logo-wechat","svgCode": "\n\n\n\n","codepoint": "\\E093"}, +{"name": "logo-wecom","svgCode": "\n\n\n\n\n\n\n","codepoint": "\\E094"}, +{"name": "logo-windows-filled","svgCode": "\n\n\n\n\n\n","codepoint": "\\E095"}, +{"name": "logo-windows","svgCode": "\n\n\n","codepoint": "\\E096"}, +{"name": "logout","svgCode": "\n\n\n\n","codepoint": "\\E097"}, +{"name": "mail","svgCode": "\n\n\n","codepoint": "\\E098"}, +{"name": "menu-fold","svgCode": "\n\n\n\n\n\n","codepoint": "\\E099"}, +{"name": "menu-unfold","svgCode": "\n\n\n\n\n\n","codepoint": "\\E09A"}, +{"name": "minus-circle-filled","svgCode": "\n\n\n","codepoint": "\\E09B"}, +{"name": "minus-circle","svgCode": "\n\n\n\n","codepoint": "\\E09C"}, +{"name": "minus-rectangle-filled","svgCode": "\n\n\n","codepoint": "\\E09D"}, +{"name": "minus-rectangle","svgCode": "\n\n\n\n","codepoint": "\\E09E"}, +{"name": "mirror","svgCode": "\n\n\n\n\n","codepoint": "\\E09F"}, +{"name": "mobile-vibrate","svgCode": "\n\n\n\n\n\n","codepoint": "\\E0A0"}, +{"name": "mobile","svgCode": "\n\n\n\n","codepoint": "\\E0A1"}, +{"name": "money-circle","svgCode": "\n\n\n\n","codepoint": "\\E0A2"}, +{"name": "more","svgCode": "\n\n\n\n\n","codepoint": "\\E0A3"}, +{"name": "move","svgCode": "\n\n\n\n\n\n\n\n\n\n","codepoint": "\\E0A4"}, +{"name": "next","svgCode": "\n\n\n\n","codepoint": "\\E0A5"}, +{"name": "notification-filled","svgCode": "\n\n\n","codepoint": "\\E0A6"}, +{"name": "notification","svgCode": "\n\n\n","codepoint": "\\E0A7"}, +{"name": "order-adjustment-column","svgCode": "\n\n\n","codepoint": "\\E0A8"}, +{"name": "order-ascending","svgCode": "\n\n\n\n\n\n","codepoint": "\\E0A9"}, +{"name": "order-descending","svgCode": "\n\n\n\n\n\n","codepoint": "\\E0AA"}, +{"name": "page-first","svgCode": "\n\n\n","codepoint": "\\E0AB"}, +{"name": "page-last","svgCode": "\n\n\n","codepoint": "\\E0AC"}, +{"name": "pause-circle-filled","svgCode": "\n\n\n","codepoint": "\\E0AD"}, +{"name": "photo","svgCode": "\n\n\n\n","codepoint": "\\E0AE"}, +{"name": "pin-filled","svgCode": "\n\n\n","codepoint": "\\E0AF"}, +{"name": "pin","svgCode": "\n\n\n","codepoint": "\\E0B0"}, +{"name": "play-circle-filled","svgCode": "\n\n\n","codepoint": "\\E0B1"}, +{"name": "play-circle-stroke","svgCode": "\n\n\n\n\n\n","codepoint": "\\E0B2"}, +{"name": "play-circle","svgCode": "\n\n\n\n\n\n","codepoint": "\\E0B3"}, +{"name": "play","svgCode": "\n\n\n","codepoint": "\\E0B4"}, +{"name": "poweroff","svgCode": "\n\n\n\n","codepoint": "\\E0B5"}, +{"name": "precise-monitor","svgCode": "\n\n\n\n\n","codepoint": "\\E0B6"}, +{"name": "previous","svgCode": "\n\n\n\n","codepoint": "\\E0B7"}, +{"name": "print","svgCode": "\n\n\n","codepoint": "\\E0B8"}, +{"name": "qrcode","svgCode": "\n\n\n\n\n\n\n\n\n\n","codepoint": "\\E0B9"}, +{"name": "queue","svgCode": "\n\n\n\n\n","codepoint": "\\E0BA"}, +{"name": "rectangle","svgCode": "\n \n\n ","codepoint": "\\E0BB"}, +{"name": "refresh","svgCode": "\n\n\n\n","codepoint": "\\E0BC"}, +{"name": "relativity","svgCode": "\n \n\n ","codepoint": "\\E0BD"}, +{"name": "remove","svgCode": "\n\n\n","codepoint": "\\E0BE"}, +{"name": "rollback","svgCode": "\n\n\n","codepoint": "\\E0BF"}, +{"name": "rollfront","svgCode": "\n\n\n","codepoint": "\\E0C0"}, +{"name": "root-list","svgCode": "\n\n\n\n\n\n\n","codepoint": "\\E0C1"}, +{"name": "rotation","svgCode": "\n\n\n","codepoint": "\\E0C2"}, +{"name": "round","svgCode": "\n\n\n","codepoint": "\\E0C3"}, +{"name": "save","svgCode": "\n\n\n","codepoint": "\\E0C4"}, +{"name": "scan","svgCode": "\n\n\n\n\n","codepoint": "\\E0C5"}, +{"name": "search","svgCode": "\n\n\n","codepoint": "\\E0C6"}, +{"name": "secured","svgCode": "\n\n\n\n","codepoint": "\\E0C7"}, +{"name": "server","svgCode": "\n\n\n\n\n\n","codepoint": "\\E0C8"}, +{"name": "service","svgCode": "\n\n\n","codepoint": "\\E0C9"}, +{"name": "setting","svgCode": "\n\n\n\n","codepoint": "\\E0CA"}, +{"name": "share","svgCode": "\n\n\n","codepoint": "\\E0CB"}, +{"name": "shop","svgCode": "\n\n\n","codepoint": "\\E0CC"}, +{"name": "slash","svgCode": "\n\n\n","codepoint": "\\E0CD"}, +{"name": "sound","svgCode": "\n\n\n\n\n","codepoint": "\\E0CE"}, +{"name": "star-filled","svgCode": "\n\n\n","codepoint": "\\E0CF"}, +{"name": "star","svgCode": "\n\n\n","codepoint": "\\E0D0"}, +{"name": "stop-circle-1","svgCode": "\n\n\n\n","codepoint": "\\E0D1"}, +{"name": "stop-circle-filled","svgCode": "\n\n\n","codepoint": "\\E0D2"}, +{"name": "stop-circle","svgCode": "\n\n\n\n\n","codepoint": "\\E0D3"}, +{"name": "stop","svgCode": "\n\n\n\n","codepoint": "\\E0D4"}, +{"name": "swap-left","svgCode": "\n\n\n","codepoint": "\\E0D5"}, +{"name": "swap-right","svgCode": "\n\n\n","codepoint": "\\E0D6"}, +{"name": "swap","svgCode": "\n\n\n\n","codepoint": "\\E0D7"}, +{"name": "thumb-down","svgCode": "\n\n\n","codepoint": "\\E0D8"}, +{"name": "thumb-up","svgCode": "\n\n\n","codepoint": "\\E0D9"}, +{"name": "time-filled","svgCode": "\n\n\n","codepoint": "\\E0DA"}, +{"name": "time","svgCode": "\n\n\n\n","codepoint": "\\E0DB"}, +{"name": "tips","svgCode": "\n\n\n\n","codepoint": "\\E0DC"}, +{"name": "tools","svgCode": "\n\n\n","codepoint": "\\E0DD"}, +{"name": "translate-1","svgCode": "\n\n\n","codepoint": "\\E0DE"}, +{"name": "translate","svgCode": "\n\n\n","codepoint": "\\E0DF"}, +{"name": "unfold-less","svgCode": "\n\n\n\n","codepoint": "\\E0E0"}, +{"name": "unfold-more","svgCode": "\n\n\n\n","codepoint": "\\E0E1"}, +{"name": "upload","svgCode": "\n\n\n\n","codepoint": "\\E0E2"}, +{"name": "usb","svgCode": "\n\n\n\n\n","codepoint": "\\E0E3"}, +{"name": "user-add","svgCode": "\n\n\n\n\n","codepoint": "\\E0E4"}, +{"name": "user-avatar","svgCode": "\n\n\n\n\n","codepoint": "\\E0E5"}, +{"name": "user-circle","svgCode": "\n\n\n\n\n\n","codepoint": "\\E0E6"}, +{"name": "user-clear","svgCode": "\n\n\n\n\n","codepoint": "\\E0E7"}, +{"name": "user-talk","svgCode": "\n\n\n\n\n\n","codepoint": "\\E0E8"}, +{"name": "user","svgCode": "\n\n\n\n","codepoint": "\\E0E9"}, +{"name": "usergroup-add","svgCode": "\n\n\n\n\n\n\n","codepoint": "\\E0EA"}, +{"name": "usergroup-clear","svgCode": "\n\n\n\n\n\n\n","codepoint": "\\E0EB"}, +{"name": "usergroup","svgCode": "\n\n\n\n\n\n","codepoint": "\\E0EC"}, +{"name": "video","svgCode": "\n\n\n\n","codepoint": "\\E0ED"}, +{"name": "view-column","svgCode": "\n\n\n","codepoint": "\\E0EE"}, +{"name": "view-list","svgCode": "\n\n\n\n\n","codepoint": "\\E0EF"}, +{"name": "view-module","svgCode": "\n\n\n\n\n","codepoint": "\\E0F0"}, +{"name": "wallet","svgCode": "\n\n\n","codepoint": "\\E0F1"}, +{"name": "wifi","svgCode": "\n\n\n\n\n\n","codepoint": "\\E0F2"}, +{"name": "zoom-in","svgCode": "\n\n\n\n","codepoint": "\\E0F3"}, +{"name": "zoom-out","svgCode": "\n\n\n\n","codepoint": "\\E0F4"}]} \ No newline at end of file diff --git a/tdesign-component/example/shell/theme/css2JsonTheme.dart b/tdesign-component/example/shell/theme/css2JsonTheme.dart new file mode 100644 index 000000000..76084f154 --- /dev/null +++ b/tdesign-component/example/shell/theme/css2JsonTheme.dart @@ -0,0 +1,159 @@ +import 'dart:convert'; +import 'dart:io'; + +void main() { + final currentDirectory = Directory.current; + print('当前运行文件所在的路径:${currentDirectory.path}'); + var basePath = '${currentDirectory.path}/shell/theme/'; + final greenFilePath = '${basePath}green.css'; + final redFilePath = '${basePath}red.css'; + // final jsonFilePath = '${basePath}cssTheme.json'; + final themeFilePath = '${currentDirectory.path}/assets/theme.json'; + + genThemeJson(items: [ + ThemeItem(name: 'green', cssPath: greenFilePath), + ThemeItem(name: 'red', cssPath: redFilePath), + ], output: themeFilePath); +} + +class ThemeItem { + String name; + String cssPath; + + ThemeItem({required this.name, required this.cssPath}); +} + +void genThemeJson({required List items, required String output}) { + var outputMap = {}; + final outputFile = File(output); + for (var item in items) { + final cssFile = File(item.cssPath); + // final jsonFile = File(jsonFilePath); + print('cssFilePath:${item.cssPath}'); + // print('jsonFilePath:${jsonFilePath}'); + + if (!cssFile.existsSync()) { + print('CSS file does not exist.'); + return; + } + + var cssContent = cssFile.readAsStringSync(); + + // 过滤深色模式的配置 + cssContent = cssContent.split("[theme-mode=\"dark\"]")[0]; + + final jsonMap = convertCssToJson(cssContent); + + var filterMap = {}; + var colorKeys = ['brand', 'warning', 'error', 'success', 'gray']; + jsonMap.forEach((key, value) { + for (var element in colorKeys) { + if (key.startsWith('--td-$element-color')) { + var newKey = convertToCamelCase(key); + var colorString = value.toString().replaceAll(';', ''); + filterMap[newKey] = colorString; + break; + } + } + }); + + var functionNames = ['Light','Focus','Disabled','Hover','Active']; + var defaultNames = ['brandColor','warningColor','errorColor','successColor']; + var refMap = {}; + var removeKey = []; + filterMap.forEach((key, value) { + if (value.contains('var(')) { + var field = value.replaceAll('var(', '').replaceAll(')', ''); + for (var f in functionNames) { + if (key.endsWith(f)) { + // 替换brandColorLight格式命名为brandLightColor + var reKey = key.replaceAll('Color$f', '${f}Color'); + refMap[reKey] = convertToCamelCase(field); + removeKey.add(key); + return; + } + } + for (var d in defaultNames){ + if(key == d){ + // 替换brandColor格式命名为brandNormalColor + var reKey = key.replaceAll('Color', 'NormalColor'); + refMap[reKey] = convertToCamelCase(field); + removeKey.add(key); + return; + } + } + refMap[key] = convertToCamelCase(field); + removeKey.add(key); + } + }); + // 清除已处理的Key + removeKey.forEach((key){ + filterMap.remove(key); + }); + var themeMap = {}; + themeMap['ref'] = refMap; + themeMap['color'] = filterMap; + + outputMap[item.name] = themeMap; + } + + outputFile.writeAsStringSync(json.encode(outputMap)); +} + +int? toColorInt(String colorStr, {double alpha = 1}) { + try { + var hexColor = colorStr.toUpperCase().replaceAll('#', ''); + if (hexColor.length == 6) { + if (alpha < 0) { + alpha = 0; + } else if (alpha > 1) { + alpha = 1; + } + var alphaInt = (0xFF * alpha).toInt(); + var alphaString = alphaInt.toRadixString(16); + + hexColor = '$alphaString$hexColor'; + } + return int.parse(hexColor, radix: 16); + } catch (e) { + // Log.w('toColor', 'error: $e'); + } + return null; +} + +String convertToCamelCase(String input) { + input = input.replaceAll('--td-', ''); + final parts = input.split('-'); + final result = StringBuffer(parts[0]); + + for (var i = 1; i < parts.length; i++) { + final part = parts[i]; + if (part.isNotEmpty) { + final camelCasePart = part[0].toUpperCase() + part.substring(1); + result.write(camelCasePart); + } + } + + return result.toString(); +} + +Map convertCssToJson(String cssContent) { + final jsonMap = {}; + + final lines = cssContent.split('\n'); + for (final line in lines) { + final trimmedLine = line.trim(); + if (trimmedLine.isNotEmpty && !trimmedLine.startsWith('//')) { + final parts = trimmedLine.split(':'); + if (parts.length == 2) { + final key = parts[0].trim(); + final value = parts[1].trim(); + jsonMap[key] = value; + } + } + } + + return jsonMap; + // final jsonString = json.encode(jsonMap); + // return jsonString; +} diff --git a/tdesign-component/example/shell/theme/green.css b/tdesign-component/example/shell/theme/green.css new file mode 100644 index 000000000..e77a31889 --- /dev/null +++ b/tdesign-component/example/shell/theme/green.css @@ -0,0 +1,466 @@ +:root,:root[theme-mode="light"] { + --brand-main: var(--td-brand-color-4); + --td-brand-color-light: var(--td-brand-color-1); + --td-brand-color-focus: var(--td-brand-color-2); + --td-brand-color-disabled: var(--td-brand-color-3); + --td-brand-color-hover: var(--td-brand-color-3); + --td-brand-color: var(--td-brand-color-4); + --td-brand-color-active: var(--td-brand-color-5); + --td-brand-color-1: #e4f9e9; + --td-brand-color-2: #c8f2d7; + --td-brand-color-3: #94dab2; + --td-brand-color-4: #45c58b; + --td-brand-color-5: #33a371; + --td-brand-color-6: #008857; + --td-brand-color-7: #006c44; + --td-brand-color-8: #005333; + --td-brand-color-9: #003b23; + --td-brand-color-10: #002515; + --td-warning-color-1: #fef3e6; + --td-warning-color-2: #f9e0c7; + --td-warning-color-3: #f7c797; + --td-warning-color-4: #f2995f; + --td-warning-color-5: #ed7b2f; + --td-warning-color-6: #d35a21; + --td-warning-color-7: #ba431b; + --td-warning-color-8: #9e3610; + --td-warning-color-9: #842b0b; + --td-warning-color-10: #5a1907; + --td-warning-color: var(--td-warning-color-5); + --td-warning-color-hover: var(--td-warning-color-4); + --td-warning-color-focus: var(--td-warning-color-2); + --td-warning-color-active: var(--td-warning-color-6); + --td-warning-color-disabled: var(--td-warning-color-3); + --td-warning-color-light: var(--td-warning-color-1); + --td-error-color-1: #fdecee; + --td-error-color-2: #f9d7d9; + --td-error-color-3: #f8b9be; + --td-error-color-4: #f78d94; + --td-error-color-5: #f36d78; + --td-error-color-6: #e34d59; + --td-error-color-7: #c9353f; + --td-error-color-8: #b11f26; + --td-error-color-9: #951114; + --td-error-color-10: #680506; + --td-error-color: var(--td-error-color-6); + --td-error-color-hover: var(--td-error-color-5); + --td-error-color-focus: var(--td-error-color-2); + --td-error-color-active: var(--td-error-color-7); + --td-error-color-disabled: var(--td-error-color-3); + --td-error-color-light: var(--td-error-color-1); + --td-success-color-1: #e8f8f2; + --td-success-color-2: #bcebdc; + --td-success-color-3: #85dbbe; + --td-success-color-4: #48c79c; + --td-success-color-5: #00a870; + --td-success-color-6: #078d5c; + --td-success-color-7: #067945; + --td-success-color-8: #056334; + --td-success-color-9: #044f2a; + --td-success-color-10: #033017; + --td-success-color: var(--td-success-color-5); + --td-success-color-hover: var(--td-success-color-4); + --td-success-color-focus: var(--td-success-color-2); + --td-success-color-active: var(--td-success-color-6); + --td-success-color-disabled: var(--td-success-color-3); + --td-success-color-light: var(--td-success-color-1); + --td-gray-color-1: #f3f3f3; + --td-gray-color-2: #eee; + --td-gray-color-3: #e7e7e7; + --td-gray-color-4: #dcdcdc; + --td-gray-color-5: #c5c5c5; + --td-gray-color-6: #a6a6a6; + --td-gray-color-7: #8b8b8b; + --td-gray-color-8: #777; + --td-gray-color-9: #5e5e5e; + --td-gray-color-10: #4b4b4b; + --td-gray-color-11: #383838; + --td-gray-color-12: #2c2c2c; + --td-gray-color-13: #242424; + --td-gray-color-14: #181818; + --td-bg-color-container: #fff; + --td-bg-color-container-select: #fff; + --td-bg-color-page: var(--td-gray-color-2); + --td-bg-color-container-hover: var(--td-gray-color-1); + --td-bg-color-container-active: var(--td-gray-color-3); + --td-bg-color-secondarycontainer: var(--td-gray-color-1); + --td-bg-color-secondarycontainer-hover: var(--td-gray-color-2); + --td-bg-color-secondarycontainer-active: var(--td-gray-color-4); + --td-bg-color-component: var(--td-gray-color-3); + --td-bg-color-component-hover: var(--td-gray-color-4); + --td-bg-color-component-active: var(--td-gray-color-6); + --td-bg-color-component-disabled: var(--td-gray-color-2); + --td-component-stroke: var(--td-gray-color-3); + --td-component-border: var(--td-gray-color-4); + --td-font-white-1: #ffffff; + --td-font-white-2: rgba(255, 255, 255, 0.55); + --td-font-white-3: rgba(255, 255, 255, 0.35); + --td-font-white-4: rgba(255, 255, 255, 0.22); + --td-font-gray-1: rgba(0, 0, 0, 0.9); + --td-font-gray-2: rgba(0, 0, 0, 0.6); + --td-font-gray-3: rgba(0, 0, 0, 0.4); + --td-font-gray-4: rgba(0, 0, 0, 0.26); + --td-brand-color-light-hover: var(--td-brand-color-2); + --td-warning-color-light-hover: var(--td-warning-color-2); + --td-error-color-light-hover: var(--td-error-color-2); + --td-success-color-light-hover: var(--td-success-color-2); + --td-bg-color-secondarycomponent: var(--td-gray-color-4); + --td-bg-color-secondarycomponent-hover: var(--td-gray-color-5); + --td-bg-color-secondarycomponent-active: var(--td-gray-color-6); + --td-table-shadow-color: rgba(0, 0, 0, 8%); + --td-scrollbar-color: rgba(0, 0, 0, 10%); + --td-scrollbar-hover-color: rgba(0, 0, 0, 30%); + --td-scroll-track-color: #fff; + --td-bg-color-specialcomponent: #fff; + --td-border-level-1-color: var(--td-gray-color-3); + --td-border-level-2-color: var(--td-gray-color-4); + --td-shadow-inset-top: inset 0 0.5px 0 #dcdcdc; + --td-shadow-inset-right: inset 0.5px 0 0 #dcdcdc; + --td-shadow-inset-bottom: inset 0 -0.5px 0 #dcdcdc; + --td-shadow-inset-left: inset -0.5px 0 0 #dcdcdc; + --td-mask-active: rgba(0, 0, 0, 0.6); + --td-mask-disabled: rgba(255, 255, 255, 0.6); + /* 字体配置 */ + --td-font-family: PingFang SC, Microsoft YaHei, Arial Regular; + --td-font-family-medium: PingFang SC, Microsoft YaHei, Arial Medium; + --td-font-size-link-small: 12px; + --td-font-size-link-medium: 14px; + --td-font-size-link-large: 16px; + --td-font-size-mark-small: 12px; + --td-font-size-mark-medium: 14px; + --td-font-size-body-small: 12px; + --td-font-size-body-medium: 14px; + --td-font-size-body-large: 16px; + --td-font-size-title-small: 14px; + --td-font-size-title-medium: 16px; + --td-font-size-title-large: 20px; + --td-font-size-headline-small: 24px; + --td-font-size-headline-medium: 28px; + --td-font-size-headline-large: 36px; + --td-font-size-display-medium: 48px; + --td-font-size-display-large: 64px; + --td-line-height-common: 8px; + --td-line-height-link-small: calc( var(--td-font-size-link-small) + var(--td-line-height-common) ); + --td-line-height-link-medium: calc( var(--td-font-size-link-medium) + var(--td-line-height-common) ); + --td-line-height-link-large: calc( var(--td-font-size-link-large) + var(--td-line-height-common) ); + --td-line-height-mark-small: calc( var(--td-font-size-mark-small) + var(--td-line-height-common) ); + --td-line-height-mark-medium: calc( var(--td-font-size-mark-medium) + var(--td-line-height-common) ); + --td-line-height-body-small: calc( var(--td-font-size-body-small) + var(--td-line-height-common) ); + --td-line-height-body-medium: calc( var(--td-font-size-body-medium) + var(--td-line-height-common) ); + --td-line-height-body-large: calc( var(--td-font-size-body-large) + var(--td-line-height-common) ); + --td-line-height-title-small: calc( var(--td-font-size-title-small) + var(--td-line-height-common) ); + --td-line-height-title-medium: calc( var(--td-font-size-title-medium) + var(--td-line-height-common) ); + --td-line-height-title-large: calc( var(--td-font-size-title-medium) + var(--td-line-height-common) ); + --td-line-height-headline-small: calc( var(--td-font-size-headline-small) + var(--td-line-height-common) ); + --td-line-height-headline-medium: calc( var(--td-font-size-headline-medium) + var(--td-line-height-common) ); + --td-line-height-headline-large: calc( var(--td-font-size-headline-large) + var(--td-line-height-common) ); + --td-line-height-display-medium: calc( var(--td-font-size-display-medium) + var(--td-line-height-common) ); + --td-line-height-display-large: calc( var(--td-font-size-display-large) + var(--td-line-height-common) ); + --td-font-link-small: var(--td-font-size-link-small) / var(--td-line-height-link-small) var(--td-font-family); + --td-font-link-medium: var(--td-font-size-link-medium) / var(--td-line-height-link-medium) var(--td-font-family); + --td-font-link-large: var(--td-font-size-link-large) / var(--td-line-height-link-large) var(--td-font-family); + --td-font-mark-small: 600 var(--td-font-size-mark-small) / var(--td-line-height-mark-small) var(--td-font-family); + --td-font-mark-medium: 600 var(--td-font-size-mark-medium) / var(--td-line-height-mark-medium) var(--td-font-family); + --td-font-body-small: var(--td-font-size-body-small) / var(--td-line-height-body-small) var(--td-font-family); + --td-font-body-medium: var(--td-font-size-body-medium) / var(--td-line-height-body-medium) var(--td-font-family); + --td-font-body-large: var(--td-font-size-body-large) / var(--td-line-height-body-large) var(--td-font-family); + --td-font-title-small: var(--td-font-size-title-small) / var(--td-line-height-title-small) var(--td-font-family); + --td-font-title-medium: var(--td-font-size-title-medium) / var(--td-line-height-title-medium) var(--td-font-family); + --td-font-title-large: var(--td-font-size-title-large) / var(--td-line-height-title-large) var(--td-font-family); + --td-font-headline-small: var(--td-font-size-headline-small) / var(--td-line-height-headline-small) var(--td-font-family); + --td-font-headline-medium: var(--td-font-size-headline-medium) / var(--td-line-height-headline-medium) var(--td-font-family); + --td-font-headline-large: var(--td-font-size-headline-large) / var(--td-line-height-headline-large) var(--td-font-family); + --td-font-display-medium: var(--td-font-size-display-medium) / var(--td-line-height-display-medium) var(--td-font-family); + --td-font-display-large: var(--td-font-size-display-large) / var(--td-line-height-display-large) var(--td-font-family); + /* 字体颜色 */ + --td-text-color-primary: var(--td-font-gray-1); + --td-text-color-secondary: var(--td-font-gray-2); + --td-text-color-placeholder: var(--td-font-gray-3); + --td-text-color-disabled: var(--td-font-gray-4); + --td-text-color-anti: #fff; + --td-text-color-brand: var(--td-brand-color); + --td-text-color-link: var(--td-brand-color); + /* end 字体配置 */ /* 圆角配置 */ + --td-radius-small: 2px; + --td-radius-default: 3px; + --td-radius-medium: 6px; + --td-radius-large: 9px; + --td-radius-extraLarge: 12px; + --td-radius-round: 999px; + --td-radius-circle: 50%; + /* end 圆角配置 *//* 阴影配置 */ + --td-shadow-1: 0 1px 10px rgba(0, 0, 0, .05),0 4px 5px rgba(0, 0, 0, 8%),0 2px 4px -1px rgba(0, 0, 0, 12%); + --td-shadow-2: 0 3px 14px 2px rgba(0, 0, 0, .05),0 8px 10px 1px rgba(0, 0, 0, 6%),0 5px 5px -3px rgba(0, 0, 0, 10%); + --td-shadow-3: 0 6px 30px 5px rgba(0, 0, 0, .05),0 16px 24px 2px rgba(0, 0, 0, 4%),0 8px 10px -5px rgba(0, 0, 0, 8%); + /* end 阴影配置 *//* 尺寸配置 */ + --td-size-1: 2px; + --td-size-2: 4px; + --td-size-3: 6px; + --td-size-4: 8px; + --td-size-5: 12px; + --td-size-6: 16px; + --td-size-7: 20px; + --td-size-8: 24px; + --td-size-9: 28px; + --td-size-10: 32px; + --td-size-11: 36px; + --td-size-12: 40px; + --td-size-13: 48px; + --td-size-14: 56px; + --td-size-15: 64px; + --td-size-16: 72px; + --td-comp-size-xxxs: var(--td-size-6); + --td-comp-size-xxs: var(--td-size-7); + --td-comp-size-xs: var(--td-size-8); + --td-comp-size-s: var(--td-size-9); + --td-comp-size-m: var(--td-size-10); + --td-comp-size-l: var(--td-size-11); + --td-comp-size-xl: var(--td-size-12); + --td-comp-size-xxl: var(--td-size-13); + --td-comp-size-xxxl: var(--td-size-14); + --td-comp-size-xxxxl: var(--td-size-15); + --td-comp-size-xxxxxl: var(--td-size-16); + --td-pop-padding-s: var(--td-size-2); + --td-pop-padding-m: var(--td-size-3); + --td-pop-padding-l: var(--td-size-4); + --td-pop-padding-xl: var(--td-size-5); + --td-pop-padding-xxl: var(--td-size-6); + --td-comp-paddingLR-xxs: var(--td-size-1); + --td-comp-paddingLR-xs: var(--td-size-2); + --td-comp-paddingLR-s: var(--td-size-4); + --td-comp-paddingLR-m: var(--td-size-5); + --td-comp-paddingLR-l: var(--td-size-6); + --td-comp-paddingLR-xl: var(--td-size-8); + --td-comp-paddingLR-xxl: var(--td-size-10); + --td-comp-paddingTB-xxs: var(--td-size-1); + --td-comp-paddingTB-xs: var(--td-size-2); + --td-comp-paddingTB-s: var(--td-size-4); + --td-comp-paddingTB-m: var(--td-size-5); + --td-comp-paddingTB-l: var(--td-size-6); + --td-comp-paddingTB-xl: var(--td-size-8); + --td-comp-paddingTB-xxl: var(--td-size-10); + --td-comp-margin-xxs: var(--td-size-1); + --td-comp-margin-xs: var(--td-size-2); + --td-comp-margin-s: var(--td-size-4); + --td-comp-margin-m: var(--td-size-5); + --td-comp-margin-l: var(--td-size-6); + --td-comp-margin-xl: var(--td-size-7); + --td-comp-margin-xxl: var(--td-size-8); + --td-comp-margin-xxxl: var(--td-size-10); + --td-comp-margin-xxxxl: var(--td-size-12); + /* end 尺寸配置 */ +} + +:root[theme-mode="dark"] { + --brand-main: var(--td-brand-color-6); + --td-brand-color-light: var(--td-brand-color-1); + --td-brand-color-focus: var(--td-brand-color-2); + --td-brand-color-disabled: var(--td-brand-color-3); + --td-brand-color-hover: var(--td-brand-color-5); + --td-brand-color: var(--td-brand-color-6); + --td-brand-color-active: var(--td-brand-color-7); + --td-brand-color-1: #33a37120; + --td-brand-color-2: #003b23; + --td-brand-color-3: #005333; + --td-brand-color-4: #006c44; + --td-brand-color-5: #008857; + --td-brand-color-6: #33a371; + --td-brand-color-7: #45c58b; + --td-brand-color-8: #94dab2; + --td-brand-color-9: #c8f2d7; + --td-brand-color-10: #e4f9e9; + --td-warning-color-1: #4f2a1d; + --td-warning-color-2: #582f21; + --td-warning-color-3: #733c23; + --td-warning-color-4: #a75d2b; + --td-warning-color-5: #cf6e2d; + --td-warning-color-6: #dc7633; + --td-warning-color-7: #e8935c; + --td-warning-color-8: #ecbf91; + --td-warning-color-9: #eed7bf; + --td-warning-color-10: #f3e9dc; + --td-error-color-1: #472324; + --td-error-color-2: #5e2a2d; + --td-error-color-3: #703439; + --td-error-color-4: #83383e; + --td-error-color-5: #a03f46; + --td-error-color-6: #c64751; + --td-error-color-7: #de6670; + --td-error-color-8: #ec888e; + --td-error-color-9: #edb1b6; + --td-error-color-10: #eeced0; + --td-success-color-1: #193a2a; + --td-success-color-2: #1a4230; + --td-success-color-3: #17533d; + --td-success-color-4: #0d7a55; + --td-success-color-5: #059465; + --td-success-color-6: #43af8a; + --td-success-color-7: #46bf96; + --td-success-color-8: #80d2b6; + --td-success-color-9: #b4e1d3; + --td-success-color-10: #deede8; + --td-gray-color-1: #f3f3f3; + --td-gray-color-2: #eee; + --td-gray-color-3: #e7e7e7; + --td-gray-color-4: #dcdcdc; + --td-gray-color-5: #c5c5c5; + --td-gray-color-6: #a6a6a6; + --td-gray-color-7: #8b8b8b; + --td-gray-color-8: #777; + --td-gray-color-9: #5e5e5e; + --td-gray-color-10: #4b4b4b; + --td-gray-color-11: #383838; + --td-gray-color-12: #2c2c2c; + --td-gray-color-13: #242424; + --td-gray-color-14: #181818; + --td-bg-color-page: var(--td-gray-color-14); + --td-bg-color-container: var(--td-gray-color-13); + --td-bg-color-container-hover: var(--td-gray-color-12); + --td-bg-color-container-active: var(--td-gray-color-10); + --td-bg-color-container-select: var(--td-gray-color-9); + --td-bg-color-secondarycontainer: var(--td-gray-color-12); + --td-bg-color-secondarycontainer-hover: var(--td-gray-color-11); + --td-bg-color-secondarycontainer-active: var(--td-gray-color-9); + --td-bg-color-component: var(--td-gray-color-11); + --td-bg-color-component-hover: var(--td-gray-color-10); + --td-bg-color-component-active: var(--td-gray-color-9); + --td-bg-color-component-disabled: var(--td-gray-color-12); + --td-component-stroke: var(--td-gray-color-11); + --td-component-border: var(--td-gray-color-9); + --td-font-white-1: rgba(255, 255, 255, 0.9); + --td-font-white-2: rgba(255, 255, 255, 0.55); + --td-font-white-3: rgba(255, 255, 255, 0.35); + --td-font-white-4: rgba(255, 255, 255, 0.22); + --td-font-gray-1: rgba(0, 0, 0, 0.9); + --td-font-gray-2: rgba(0, 0, 0, 0.6); + --td-font-gray-3: rgba(0, 0, 0, 0.4); + --td-font-gray-4: rgba(0, 0, 0, 0.26); + --td-gray-color-1: #f3f3f3; + --td-gray-color-2: #eee; + --td-gray-color-3: #e7e7e7; + --td-gray-color-4: #dcdcdc; + --td-gray-color-5: #c5c5c5; + --td-gray-color-6: #a6a6a6; + --td-gray-color-7: #8b8b8b; + --td-gray-color-8: #777; + --td-gray-color-9: #5e5e5e; + --td-gray-color-10: #4b4b4b; + --td-gray-color-11: #383838; + --td-gray-color-12: #2c2c2c; + --td-gray-color-13: #242424; + --td-gray-color-14: #181818; + --td-bg-color-page: var(--td-gray-color-14); + --td-bg-color-container: var(--td-gray-color-13); + --td-bg-color-container-hover: var(--td-gray-color-12); + --td-bg-color-container-active: var(--td-gray-color-10); + --td-bg-color-container-select: var(--td-gray-color-9); + --td-bg-color-secondarycontainer: var(--td-gray-color-12); + --td-bg-color-secondarycontainer-hover: var(--td-gray-color-11); + --td-bg-color-secondarycontainer-active: var(--td-gray-color-9); + --td-bg-color-component: var(--td-gray-color-11); + --td-bg-color-component-hover: var(--td-gray-color-10); + --td-bg-color-component-active: var(--td-gray-color-9); + --td-bg-color-secondarycomponent: var(--td-gray-color-10); + --td-bg-color-secondarycomponent-hover: var(--td-gray-color-9); + --td-bg-color-secondarycomponent-active: var(--td-gray-color-8); + --td-bg-color-component-disabled: var(--td-gray-color-12); + --td-component-stroke: var(--td-gray-color-11); + --td-component-border: var(--td-gray-color-9); + --td-font-white-1: rgba(255, 255, 255, 0.9); + --td-font-white-2: rgba(255, 255, 255, 0.55); + --td-font-white-3: rgba(255, 255, 255, 0.35); + --td-font-white-4: rgba(255, 255, 255, 0.22); + --td-font-gray-1: rgba(0, 0, 0, 0.9); + --td-font-gray-2: rgba(0, 0, 0, 0.6); + --td-font-gray-3: rgba(0, 0, 0, 0.4); + --td-font-gray-4: rgba(0, 0, 0, 0.26); + --td-text-color-primary: var(--td-font-white-1); + --td-text-color-secondary: var(--td-font-white-2); + --td-text-color-placeholder: var(--td-font-white-3); + --td-text-color-disabled: var(--td-font-white-4); + --td-text-color-anti: #fff; + --td-text-color-brand: var(--td-brand-color); + --td-text-color-link: var(--td-brand-color); + --td-table-shadow-color: rgba(0, 0, 0, 55%); + --td-scrollbar-color: rgba(255, 255, 255, 10%); + --td-scrollbar-hover-color: rgba(255, 255, 255, 30%); + --td-scroll-track-color: #333; + --td-bg-color-specialcomponent: transparent; + --td-border-level-1-color: var(--td-gray-color-11); + --td-border-level-2-color: var(--td-gray-color-9); + --td-mask-active: rgba(0, 0, 0, 0.4); + --td-mask-disabled: rgba(0, 0, 0, 0.6); + --td-shadow-inset-top: inset 0 0.5px 0 #5e5e5e; + --td-shadow-inset-right: inset 0.5px 0 0 #5e5e5e; + --td-shadow-inset-bottom: inset 0 -0.5px 0 #5e5e5e; + --td-shadow-inset-left: inset -0.5px 0 0 #5e5e5e; + /* 圆角配置 */ + --td-radius-small: 2px; + --td-radius-default: 3px; + --td-radius-medium: 6px; + --td-radius-large: 9px; + --td-radius-extraLarge: 12px; + --td-radius-round: 999px; + --td-radius-circle: 50%; + /* end 圆角配置 *//* 阴影配置 */ + --td-shadow-1: 0 1px 10px rgba(0, 0, 0, .05),0 4px 5px rgba(0, 0, 0, 8%),0 2px 4px -1px rgba(0, 0, 0, 12%); + --td-shadow-2: 0 3px 14px 2px rgba(0, 0, 0, .05),0 8px 10px 1px rgba(0, 0, 0, 6%),0 5px 5px -3px rgba(0, 0, 0, 10%); + --td-shadow-3: 0 6px 30px 5px rgba(0, 0, 0, .05),0 16px 24px 2px rgba(0, 0, 0, 4%),0 8px 10px -5px rgba(0, 0, 0, 8%); + /* end 阴影配置 *//* 尺寸配置 */ + --td-size-1: 2px; + --td-size-2: 4px; + --td-size-3: 6px; + --td-size-4: 8px; + --td-size-5: 12px; + --td-size-6: 16px; + --td-size-7: 20px; + --td-size-8: 24px; + --td-size-9: 28px; + --td-size-10: 32px; + --td-size-11: 36px; + --td-size-12: 40px; + --td-size-13: 48px; + --td-size-14: 56px; + --td-size-15: 64px; + --td-size-16: 72px; + --td-comp-size-xxxs: var(--td-size-6); + --td-comp-size-xxs: var(--td-size-7); + --td-comp-size-xs: var(--td-size-8); + --td-comp-size-s: var(--td-size-9); + --td-comp-size-m: var(--td-size-10); + --td-comp-size-l: var(--td-size-11); + --td-comp-size-xl: var(--td-size-12); + --td-comp-size-xxl: var(--td-size-13); + --td-comp-size-xxxl: var(--td-size-14); + --td-comp-size-xxxxl: var(--td-size-15); + --td-comp-size-xxxxxl: var(--td-size-16); + --td-pop-padding-s: var(--td-size-2); + --td-pop-padding-m: var(--td-size-3); + --td-pop-padding-l: var(--td-size-4); + --td-pop-padding-xl: var(--td-size-5); + --td-pop-padding-xxl: var(--td-size-6); + --td-comp-paddingLR-xxs: var(--td-size-1); + --td-comp-paddingLR-xs: var(--td-size-2); + --td-comp-paddingLR-s: var(--td-size-4); + --td-comp-paddingLR-m: var(--td-size-5); + --td-comp-paddingLR-l: var(--td-size-6); + --td-comp-paddingLR-xl: var(--td-size-8); + --td-comp-paddingLR-xxl: var(--td-size-10); + --td-comp-paddingTB-xxs: var(--td-size-1); + --td-comp-paddingTB-xs: var(--td-size-2); + --td-comp-paddingTB-s: var(--td-size-4); + --td-comp-paddingTB-m: var(--td-size-5); + --td-comp-paddingTB-l: var(--td-size-6); + --td-comp-paddingTB-xl: var(--td-size-8); + --td-comp-paddingTB-xxl: var(--td-size-10); + --td-comp-margin-xxs: var(--td-size-1); + --td-comp-margin-xs: var(--td-size-2); + --td-comp-margin-s: var(--td-size-4); + --td-comp-margin-m: var(--td-size-5); + --td-comp-margin-l: var(--td-size-6); + --td-comp-margin-xl: var(--td-size-7); + --td-comp-margin-xxl: var(--td-size-8); + --td-comp-margin-xxxl: var(--td-size-10); + --td-comp-margin-xxxxl: var(--td-size-12); + /* end 尺寸配置 */ +} \ No newline at end of file diff --git a/tdesign-component/example/shell/theme/red.css b/tdesign-component/example/shell/theme/red.css new file mode 100644 index 000000000..79898d6f5 --- /dev/null +++ b/tdesign-component/example/shell/theme/red.css @@ -0,0 +1,466 @@ +:root,:root[theme-mode="light"] { + --brand-main: var(--td-brand-color-5); + --td-brand-color-light: var(--td-brand-color-1); + --td-brand-color-focus: var(--td-brand-color-2); + --td-brand-color-disabled: var(--td-brand-color-3); + --td-brand-color-hover: var(--td-brand-color-4); + --td-brand-color: var(--td-brand-color-5); + --td-brand-color-active: var(--td-brand-color-6); + --td-brand-color-1: #fff0f1; + --td-brand-color-2: #ffd8dd; + --td-brand-color-3: #ffb7c1; + --td-brand-color-4: #ff8fa2; + --td-brand-color-5: #ff5479; + --td-brand-color-6: #db3d62; + --td-brand-color-7: #b2294b; + --td-brand-color-8: #8d1135; + --td-brand-color-9: #690021; + --td-brand-color-10: #480014; + --td-warning-color-1: #fef3e6; + --td-warning-color-2: #f9e0c7; + --td-warning-color-3: #f7c797; + --td-warning-color-4: #f2995f; + --td-warning-color-5: #ed7b2f; + --td-warning-color-6: #d35a21; + --td-warning-color-7: #ba431b; + --td-warning-color-8: #9e3610; + --td-warning-color-9: #842b0b; + --td-warning-color-10: #5a1907; + --td-warning-color: var(--td-warning-color-5); + --td-warning-color-hover: var(--td-warning-color-4); + --td-warning-color-focus: var(--td-warning-color-2); + --td-warning-color-active: var(--td-warning-color-6); + --td-warning-color-disabled: var(--td-warning-color-3); + --td-warning-color-light: var(--td-warning-color-1); + --td-error-color-1: #fdecee; + --td-error-color-2: #f9d7d9; + --td-error-color-3: #f8b9be; + --td-error-color-4: #f78d94; + --td-error-color-5: #f36d78; + --td-error-color-6: #e34d59; + --td-error-color-7: #c9353f; + --td-error-color-8: #b11f26; + --td-error-color-9: #951114; + --td-error-color-10: #680506; + --td-error-color: var(--td-error-color-6); + --td-error-color-hover: var(--td-error-color-5); + --td-error-color-focus: var(--td-error-color-2); + --td-error-color-active: var(--td-error-color-7); + --td-error-color-disabled: var(--td-error-color-3); + --td-error-color-light: var(--td-error-color-1); + --td-success-color-1: #e8f8f2; + --td-success-color-2: #bcebdc; + --td-success-color-3: #85dbbe; + --td-success-color-4: #48c79c; + --td-success-color-5: #00a870; + --td-success-color-6: #078d5c; + --td-success-color-7: #067945; + --td-success-color-8: #056334; + --td-success-color-9: #044f2a; + --td-success-color-10: #033017; + --td-success-color: var(--td-success-color-5); + --td-success-color-hover: var(--td-success-color-4); + --td-success-color-focus: var(--td-success-color-2); + --td-success-color-active: var(--td-success-color-6); + --td-success-color-disabled: var(--td-success-color-3); + --td-success-color-light: var(--td-success-color-1); + --td-gray-color-1: #f3f3f3; + --td-gray-color-2: #eee; + --td-gray-color-3: #e7e7e7; + --td-gray-color-4: #dcdcdc; + --td-gray-color-5: #c5c5c5; + --td-gray-color-6: #a6a6a6; + --td-gray-color-7: #8b8b8b; + --td-gray-color-8: #777; + --td-gray-color-9: #5e5e5e; + --td-gray-color-10: #4b4b4b; + --td-gray-color-11: #383838; + --td-gray-color-12: #2c2c2c; + --td-gray-color-13: #242424; + --td-gray-color-14: #181818; + --td-bg-color-container: #fff; + --td-bg-color-container-select: #fff; + --td-bg-color-page: var(--td-gray-color-2); + --td-bg-color-container-hover: var(--td-gray-color-1); + --td-bg-color-container-active: var(--td-gray-color-3); + --td-bg-color-secondarycontainer: var(--td-gray-color-1); + --td-bg-color-secondarycontainer-hover: var(--td-gray-color-2); + --td-bg-color-secondarycontainer-active: var(--td-gray-color-4); + --td-bg-color-component: var(--td-gray-color-3); + --td-bg-color-component-hover: var(--td-gray-color-4); + --td-bg-color-component-active: var(--td-gray-color-6); + --td-bg-color-component-disabled: var(--td-gray-color-2); + --td-component-stroke: var(--td-gray-color-3); + --td-component-border: var(--td-gray-color-4); + --td-font-white-1: #ffffff; + --td-font-white-2: rgba(255, 255, 255, 0.55); + --td-font-white-3: rgba(255, 255, 255, 0.35); + --td-font-white-4: rgba(255, 255, 255, 0.22); + --td-font-gray-1: rgba(0, 0, 0, 0.9); + --td-font-gray-2: rgba(0, 0, 0, 0.6); + --td-font-gray-3: rgba(0, 0, 0, 0.4); + --td-font-gray-4: rgba(0, 0, 0, 0.26); + --td-brand-color-light-hover: var(--td-brand-color-2); + --td-warning-color-light-hover: var(--td-warning-color-2); + --td-error-color-light-hover: var(--td-error-color-2); + --td-success-color-light-hover: var(--td-success-color-2); + --td-bg-color-secondarycomponent: var(--td-gray-color-4); + --td-bg-color-secondarycomponent-hover: var(--td-gray-color-5); + --td-bg-color-secondarycomponent-active: var(--td-gray-color-6); + --td-table-shadow-color: rgba(0, 0, 0, 8%); + --td-scrollbar-color: rgba(0, 0, 0, 10%); + --td-scrollbar-hover-color: rgba(0, 0, 0, 30%); + --td-scroll-track-color: #fff; + --td-bg-color-specialcomponent: #fff; + --td-border-level-1-color: var(--td-gray-color-3); + --td-border-level-2-color: var(--td-gray-color-4); + --td-shadow-inset-top: inset 0 0.5px 0 #dcdcdc; + --td-shadow-inset-right: inset 0.5px 0 0 #dcdcdc; + --td-shadow-inset-bottom: inset 0 -0.5px 0 #dcdcdc; + --td-shadow-inset-left: inset -0.5px 0 0 #dcdcdc; + --td-mask-active: rgba(0, 0, 0, 0.6); + --td-mask-disabled: rgba(255, 255, 255, 0.6); + /* 字体配置 */ + --td-font-family: PingFang SC, Microsoft YaHei, Arial Regular; + --td-font-family-medium: PingFang SC, Microsoft YaHei, Arial Medium; + --td-font-size-link-small: 12px; + --td-font-size-link-medium: 14px; + --td-font-size-link-large: 16px; + --td-font-size-mark-small: 12px; + --td-font-size-mark-medium: 14px; + --td-font-size-body-small: 12px; + --td-font-size-body-medium: 14px; + --td-font-size-body-large: 16px; + --td-font-size-title-small: 14px; + --td-font-size-title-medium: 16px; + --td-font-size-title-large: 20px; + --td-font-size-headline-small: 24px; + --td-font-size-headline-medium: 28px; + --td-font-size-headline-large: 36px; + --td-font-size-display-medium: 48px; + --td-font-size-display-large: 64px; + --td-line-height-common: 8px; + --td-line-height-link-small: calc( var(--td-font-size-link-small) + var(--td-line-height-common) ); + --td-line-height-link-medium: calc( var(--td-font-size-link-medium) + var(--td-line-height-common) ); + --td-line-height-link-large: calc( var(--td-font-size-link-large) + var(--td-line-height-common) ); + --td-line-height-mark-small: calc( var(--td-font-size-mark-small) + var(--td-line-height-common) ); + --td-line-height-mark-medium: calc( var(--td-font-size-mark-medium) + var(--td-line-height-common) ); + --td-line-height-body-small: calc( var(--td-font-size-body-small) + var(--td-line-height-common) ); + --td-line-height-body-medium: calc( var(--td-font-size-body-medium) + var(--td-line-height-common) ); + --td-line-height-body-large: calc( var(--td-font-size-body-large) + var(--td-line-height-common) ); + --td-line-height-title-small: calc( var(--td-font-size-title-small) + var(--td-line-height-common) ); + --td-line-height-title-medium: calc( var(--td-font-size-title-medium) + var(--td-line-height-common) ); + --td-line-height-title-large: calc( var(--td-font-size-title-medium) + var(--td-line-height-common) ); + --td-line-height-headline-small: calc( var(--td-font-size-headline-small) + var(--td-line-height-common) ); + --td-line-height-headline-medium: calc( var(--td-font-size-headline-medium) + var(--td-line-height-common) ); + --td-line-height-headline-large: calc( var(--td-font-size-headline-large) + var(--td-line-height-common) ); + --td-line-height-display-medium: calc( var(--td-font-size-display-medium) + var(--td-line-height-common) ); + --td-line-height-display-large: calc( var(--td-font-size-display-large) + var(--td-line-height-common) ); + --td-font-link-small: var(--td-font-size-link-small) / var(--td-line-height-link-small) var(--td-font-family); + --td-font-link-medium: var(--td-font-size-link-medium) / var(--td-line-height-link-medium) var(--td-font-family); + --td-font-link-large: var(--td-font-size-link-large) / var(--td-line-height-link-large) var(--td-font-family); + --td-font-mark-small: 600 var(--td-font-size-mark-small) / var(--td-line-height-mark-small) var(--td-font-family); + --td-font-mark-medium: 600 var(--td-font-size-mark-medium) / var(--td-line-height-mark-medium) var(--td-font-family); + --td-font-body-small: var(--td-font-size-body-small) / var(--td-line-height-body-small) var(--td-font-family); + --td-font-body-medium: var(--td-font-size-body-medium) / var(--td-line-height-body-medium) var(--td-font-family); + --td-font-body-large: var(--td-font-size-body-large) / var(--td-line-height-body-large) var(--td-font-family); + --td-font-title-small: var(--td-font-size-title-small) / var(--td-line-height-title-small) var(--td-font-family); + --td-font-title-medium: var(--td-font-size-title-medium) / var(--td-line-height-title-medium) var(--td-font-family); + --td-font-title-large: var(--td-font-size-title-large) / var(--td-line-height-title-large) var(--td-font-family); + --td-font-headline-small: var(--td-font-size-headline-small) / var(--td-line-height-headline-small) var(--td-font-family); + --td-font-headline-medium: var(--td-font-size-headline-medium) / var(--td-line-height-headline-medium) var(--td-font-family); + --td-font-headline-large: var(--td-font-size-headline-large) / var(--td-line-height-headline-large) var(--td-font-family); + --td-font-display-medium: var(--td-font-size-display-medium) / var(--td-line-height-display-medium) var(--td-font-family); + --td-font-display-large: var(--td-font-size-display-large) / var(--td-line-height-display-large) var(--td-font-family); + /* 字体颜色 */ + --td-text-color-primary: var(--td-font-gray-1); + --td-text-color-secondary: var(--td-font-gray-2); + --td-text-color-placeholder: var(--td-font-gray-3); + --td-text-color-disabled: var(--td-font-gray-4); + --td-text-color-anti: #fff; + --td-text-color-brand: var(--td-brand-color); + --td-text-color-link: var(--td-brand-color); + /* end 字体配置 */ /* 圆角配置 */ + --td-radius-small: 2px; + --td-radius-default: 3px; + --td-radius-medium: 6px; + --td-radius-large: 9px; + --td-radius-extraLarge: 12px; + --td-radius-round: 999px; + --td-radius-circle: 50%; + /* end 圆角配置 *//* 阴影配置 */ + --td-shadow-1: 0 1px 10px rgba(0, 0, 0, .05),0 4px 5px rgba(0, 0, 0, 8%),0 2px 4px -1px rgba(0, 0, 0, 12%); + --td-shadow-2: 0 3px 14px 2px rgba(0, 0, 0, .05),0 8px 10px 1px rgba(0, 0, 0, 6%),0 5px 5px -3px rgba(0, 0, 0, 10%); + --td-shadow-3: 0 6px 30px 5px rgba(0, 0, 0, .05),0 16px 24px 2px rgba(0, 0, 0, 4%),0 8px 10px -5px rgba(0, 0, 0, 8%); + /* end 阴影配置 *//* 尺寸配置 */ + --td-size-1: 2px; + --td-size-2: 4px; + --td-size-3: 6px; + --td-size-4: 8px; + --td-size-5: 12px; + --td-size-6: 16px; + --td-size-7: 20px; + --td-size-8: 24px; + --td-size-9: 28px; + --td-size-10: 32px; + --td-size-11: 36px; + --td-size-12: 40px; + --td-size-13: 48px; + --td-size-14: 56px; + --td-size-15: 64px; + --td-size-16: 72px; + --td-comp-size-xxxs: 64px; + --td-comp-size-xxs: var(--td-size-7); + --td-comp-size-xs: var(--td-size-8); + --td-comp-size-s: var(--td-size-9); + --td-comp-size-m: var(--td-size-10); + --td-comp-size-l: var(--td-size-11); + --td-comp-size-xl: var(--td-size-12); + --td-comp-size-xxl: var(--td-size-13); + --td-comp-size-xxxl: var(--td-size-14); + --td-comp-size-xxxxl: var(--td-size-15); + --td-comp-size-xxxxxl: var(--td-size-16); + --td-pop-padding-s: var(--td-size-2); + --td-pop-padding-m: var(--td-size-3); + --td-pop-padding-l: var(--td-size-4); + --td-pop-padding-xl: var(--td-size-5); + --td-pop-padding-xxl: var(--td-size-6); + --td-comp-paddingLR-xxs: var(--td-size-1); + --td-comp-paddingLR-xs: var(--td-size-2); + --td-comp-paddingLR-s: var(--td-size-4); + --td-comp-paddingLR-m: var(--td-size-5); + --td-comp-paddingLR-l: var(--td-size-6); + --td-comp-paddingLR-xl: var(--td-size-8); + --td-comp-paddingLR-xxl: var(--td-size-10); + --td-comp-paddingTB-xxs: var(--td-size-1); + --td-comp-paddingTB-xs: var(--td-size-2); + --td-comp-paddingTB-s: var(--td-size-4); + --td-comp-paddingTB-m: var(--td-size-5); + --td-comp-paddingTB-l: var(--td-size-6); + --td-comp-paddingTB-xl: var(--td-size-8); + --td-comp-paddingTB-xxl: var(--td-size-10); + --td-comp-margin-xxs: 2px; + --td-comp-margin-xs: var(--td-size-2); + --td-comp-margin-s: var(--td-size-4); + --td-comp-margin-m: var(--td-size-5); + --td-comp-margin-l: var(--td-size-6); + --td-comp-margin-xl: var(--td-size-7); + --td-comp-margin-xxl: var(--td-size-8); + --td-comp-margin-xxxl: var(--td-size-10); + --td-comp-margin-xxxxl: var(--td-size-12); + /* end 尺寸配置 */ +} + +:root[theme-mode="dark"] { + --brand-main: var(--td-brand-color-6); + --td-brand-color-light: var(--td-brand-color-1); + --td-brand-color-focus: var(--td-brand-color-2); + --td-brand-color-disabled: var(--td-brand-color-3); + --td-brand-color-hover: var(--td-brand-color-5); + --td-brand-color: var(--td-brand-color-6); + --td-brand-color-active: var(--td-brand-color-7); + --td-brand-color-1: #ff547920; + --td-brand-color-2: #690021; + --td-brand-color-3: #8d1135; + --td-brand-color-4: #b2294b; + --td-brand-color-5: #db3d62; + --td-brand-color-6: #ff5479; + --td-brand-color-7: #ff8fa2; + --td-brand-color-8: #ffb7c1; + --td-brand-color-9: #ffd8dd; + --td-brand-color-10: #fff0f1; + --td-warning-color-1: #4f2a1d; + --td-warning-color-2: #582f21; + --td-warning-color-3: #733c23; + --td-warning-color-4: #a75d2b; + --td-warning-color-5: #cf6e2d; + --td-warning-color-6: #dc7633; + --td-warning-color-7: #e8935c; + --td-warning-color-8: #ecbf91; + --td-warning-color-9: #eed7bf; + --td-warning-color-10: #f3e9dc; + --td-error-color-1: #472324; + --td-error-color-2: #5e2a2d; + --td-error-color-3: #703439; + --td-error-color-4: #83383e; + --td-error-color-5: #a03f46; + --td-error-color-6: #c64751; + --td-error-color-7: #de6670; + --td-error-color-8: #ec888e; + --td-error-color-9: #edb1b6; + --td-error-color-10: #eeced0; + --td-success-color-1: #193a2a; + --td-success-color-2: #1a4230; + --td-success-color-3: #17533d; + --td-success-color-4: #0d7a55; + --td-success-color-5: #059465; + --td-success-color-6: #43af8a; + --td-success-color-7: #46bf96; + --td-success-color-8: #80d2b6; + --td-success-color-9: #b4e1d3; + --td-success-color-10: #deede8; + --td-gray-color-1: #f3f3f3; + --td-gray-color-2: #eee; + --td-gray-color-3: #e7e7e7; + --td-gray-color-4: #dcdcdc; + --td-gray-color-5: #c5c5c5; + --td-gray-color-6: #a6a6a6; + --td-gray-color-7: #8b8b8b; + --td-gray-color-8: #777; + --td-gray-color-9: #5e5e5e; + --td-gray-color-10: #4b4b4b; + --td-gray-color-11: #383838; + --td-gray-color-12: #2c2c2c; + --td-gray-color-13: #242424; + --td-gray-color-14: #181818; + --td-bg-color-page: var(--td-gray-color-14); + --td-bg-color-container: var(--td-gray-color-13); + --td-bg-color-container-hover: var(--td-gray-color-12); + --td-bg-color-container-active: var(--td-gray-color-10); + --td-bg-color-container-select: var(--td-gray-color-9); + --td-bg-color-secondarycontainer: var(--td-gray-color-12); + --td-bg-color-secondarycontainer-hover: var(--td-gray-color-11); + --td-bg-color-secondarycontainer-active: var(--td-gray-color-9); + --td-bg-color-component: var(--td-gray-color-11); + --td-bg-color-component-hover: var(--td-gray-color-10); + --td-bg-color-component-active: var(--td-gray-color-9); + --td-bg-color-component-disabled: var(--td-gray-color-12); + --td-component-stroke: var(--td-gray-color-11); + --td-component-border: var(--td-gray-color-9); + --td-font-white-1: rgba(255, 255, 255, 0.9); + --td-font-white-2: rgba(255, 255, 255, 0.55); + --td-font-white-3: rgba(255, 255, 255, 0.35); + --td-font-white-4: rgba(255, 255, 255, 0.22); + --td-font-gray-1: rgba(0, 0, 0, 0.9); + --td-font-gray-2: rgba(0, 0, 0, 0.6); + --td-font-gray-3: rgba(0, 0, 0, 0.4); + --td-font-gray-4: rgba(0, 0, 0, 0.26); + --td-gray-color-1: #f3f3f3; + --td-gray-color-2: #eee; + --td-gray-color-3: #e7e7e7; + --td-gray-color-4: #dcdcdc; + --td-gray-color-5: #c5c5c5; + --td-gray-color-6: #a6a6a6; + --td-gray-color-7: #8b8b8b; + --td-gray-color-8: #777; + --td-gray-color-9: #5e5e5e; + --td-gray-color-10: #4b4b4b; + --td-gray-color-11: #383838; + --td-gray-color-12: #2c2c2c; + --td-gray-color-13: #242424; + --td-gray-color-14: #181818; + --td-bg-color-page: var(--td-gray-color-14); + --td-bg-color-container: var(--td-gray-color-13); + --td-bg-color-container-hover: var(--td-gray-color-12); + --td-bg-color-container-active: var(--td-gray-color-10); + --td-bg-color-container-select: var(--td-gray-color-9); + --td-bg-color-secondarycontainer: var(--td-gray-color-12); + --td-bg-color-secondarycontainer-hover: var(--td-gray-color-11); + --td-bg-color-secondarycontainer-active: var(--td-gray-color-9); + --td-bg-color-component: var(--td-gray-color-11); + --td-bg-color-component-hover: var(--td-gray-color-10); + --td-bg-color-component-active: var(--td-gray-color-9); + --td-bg-color-secondarycomponent: var(--td-gray-color-10); + --td-bg-color-secondarycomponent-hover: var(--td-gray-color-9); + --td-bg-color-secondarycomponent-active: var(--td-gray-color-8); + --td-bg-color-component-disabled: var(--td-gray-color-12); + --td-component-stroke: var(--td-gray-color-11); + --td-component-border: var(--td-gray-color-9); + --td-font-white-1: rgba(255, 255, 255, 0.9); + --td-font-white-2: rgba(255, 255, 255, 0.55); + --td-font-white-3: rgba(255, 255, 255, 0.35); + --td-font-white-4: rgba(255, 255, 255, 0.22); + --td-font-gray-1: rgba(0, 0, 0, 0.9); + --td-font-gray-2: rgba(0, 0, 0, 0.6); + --td-font-gray-3: rgba(0, 0, 0, 0.4); + --td-font-gray-4: rgba(0, 0, 0, 0.26); + --td-text-color-primary: var(--td-font-white-1); + --td-text-color-secondary: var(--td-font-white-2); + --td-text-color-placeholder: var(--td-font-white-3); + --td-text-color-disabled: var(--td-font-white-4); + --td-text-color-anti: #fff; + --td-text-color-brand: var(--td-brand-color); + --td-text-color-link: var(--td-brand-color); + --td-table-shadow-color: rgba(0, 0, 0, 55%); + --td-scrollbar-color: rgba(255, 255, 255, 10%); + --td-scrollbar-hover-color: rgba(255, 255, 255, 30%); + --td-scroll-track-color: #333; + --td-bg-color-specialcomponent: transparent; + --td-border-level-1-color: var(--td-gray-color-11); + --td-border-level-2-color: var(--td-gray-color-9); + --td-mask-active: rgba(0, 0, 0, 0.4); + --td-mask-disabled: rgba(0, 0, 0, 0.6); + --td-shadow-inset-top: inset 0 0.5px 0 #5e5e5e; + --td-shadow-inset-right: inset 0.5px 0 0 #5e5e5e; + --td-shadow-inset-bottom: inset 0 -0.5px 0 #5e5e5e; + --td-shadow-inset-left: inset -0.5px 0 0 #5e5e5e; + /* 圆角配置 */ + --td-radius-small: 2px; + --td-radius-default: 3px; + --td-radius-medium: 6px; + --td-radius-large: 9px; + --td-radius-extraLarge: 12px; + --td-radius-round: 999px; + --td-radius-circle: 50%; + /* end 圆角配置 *//* 阴影配置 */ + --td-shadow-1: 0 1px 10px rgba(0, 0, 0, .05),0 4px 5px rgba(0, 0, 0, 8%),0 2px 4px -1px rgba(0, 0, 0, 12%); + --td-shadow-2: 0 3px 14px 2px rgba(0, 0, 0, .05),0 8px 10px 1px rgba(0, 0, 0, 6%),0 5px 5px -3px rgba(0, 0, 0, 10%); + --td-shadow-3: 0 6px 30px 5px rgba(0, 0, 0, .05),0 16px 24px 2px rgba(0, 0, 0, 4%),0 8px 10px -5px rgba(0, 0, 0, 8%); + /* end 阴影配置 *//* 尺寸配置 */ + --td-size-1: 2px; + --td-size-2: 4px; + --td-size-3: 6px; + --td-size-4: 8px; + --td-size-5: 12px; + --td-size-6: 16px; + --td-size-7: 20px; + --td-size-8: 24px; + --td-size-9: 28px; + --td-size-10: 32px; + --td-size-11: 36px; + --td-size-12: 40px; + --td-size-13: 48px; + --td-size-14: 56px; + --td-size-15: 64px; + --td-size-16: 72px; + --td-comp-size-xxxs: 64px; + --td-comp-size-xxs: var(--td-size-7); + --td-comp-size-xs: var(--td-size-8); + --td-comp-size-s: var(--td-size-9); + --td-comp-size-m: var(--td-size-10); + --td-comp-size-l: var(--td-size-11); + --td-comp-size-xl: var(--td-size-12); + --td-comp-size-xxl: var(--td-size-13); + --td-comp-size-xxxl: var(--td-size-14); + --td-comp-size-xxxxl: var(--td-size-15); + --td-comp-size-xxxxxl: var(--td-size-16); + --td-pop-padding-s: var(--td-size-2); + --td-pop-padding-m: var(--td-size-3); + --td-pop-padding-l: var(--td-size-4); + --td-pop-padding-xl: var(--td-size-5); + --td-pop-padding-xxl: var(--td-size-6); + --td-comp-paddingLR-xxs: var(--td-size-1); + --td-comp-paddingLR-xs: var(--td-size-2); + --td-comp-paddingLR-s: var(--td-size-4); + --td-comp-paddingLR-m: var(--td-size-5); + --td-comp-paddingLR-l: var(--td-size-6); + --td-comp-paddingLR-xl: var(--td-size-8); + --td-comp-paddingLR-xxl: var(--td-size-10); + --td-comp-paddingTB-xxs: var(--td-size-1); + --td-comp-paddingTB-xs: var(--td-size-2); + --td-comp-paddingTB-s: var(--td-size-4); + --td-comp-paddingTB-m: var(--td-size-5); + --td-comp-paddingTB-l: var(--td-size-6); + --td-comp-paddingTB-xl: var(--td-size-8); + --td-comp-paddingTB-xxl: var(--td-size-10); + --td-comp-margin-xxs: 2px; + --td-comp-margin-xs: var(--td-size-2); + --td-comp-margin-s: var(--td-size-4); + --td-comp-margin-m: var(--td-size-5); + --td-comp-margin-l: var(--td-size-6); + --td-comp-margin-xl: var(--td-size-7); + --td-comp-margin-xxl: var(--td-size-8); + --td-comp-margin-xxxl: var(--td-size-10); + --td-comp-margin-xxxxl: var(--td-size-12); + /* end 尺寸配置 */ +} \ No newline at end of file diff --git a/tdesign-component/example/test/widget_test.dart b/tdesign-component/example/test/widget_test.dart new file mode 100644 index 000000000..18ea3e5c9 --- /dev/null +++ b/tdesign-component/example/test/widget_test.dart @@ -0,0 +1,106 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility that Flutter provides. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tdesign_flutter/tdesign_flutter.dart'; +import 'package:tdesign_flutter_example/base/web_md_tool.dart'; +import 'package:tdesign_flutter_example/config.dart'; + +import 'package:tdesign_flutter_example/home.dart'; +import 'package:tdesign_flutter_example/main.dart'; + +void main() async { + + testWidgets('Counter increments smoke testeee', (WidgetTester tester) async { + + WebMdTool.needGenerateWebMd = true; + + await tester.pumpWidget(const MyApp()); + exampleMap.forEach((key, value) { + value.forEach((model) { + if (!model.isTodo) { + examplePageList.add(model); + } + }); + }); + for(var element in examplePageList){ + // Build our app and trigger a frame. + if(element.text == '颜色'){ + // 测试结束 + break; + } + await _testComponent(tester, element.text); + } + // throw Exception('<===============执行完成!!!!=================>'); + + }); + + +} + +var changeList = ['BackTop 返回顶部','Steps 步骤条','Calendar 日历','Picker 选择器','Stepper 步进器','Upload 上传','Collapse 折叠面板','Progress 进度条','Tag 标签','Loading 加载', 'PullDownRefresh 下拉刷新']; +Finder? lastFinder; + +int count = 0; +Future _testComponent(WidgetTester tester, String name) async { + print('\n\n当前组件==============>:$name, 滑动count:$count'); + + if(changeList.contains(name) && lastFinder != null){ + count++; + await tester.fling(lastFinder!, const Offset(0, -300), 2); + try { + await tester.pumpAndSettle(); + } catch (e) { + print('pumpAndSettle 1 error:$e'); + } + } + if(name == 'Skeleton 骨架屏'){ + // TODO: 骨架屏需要额外手动处理 + return; + } + var button = find.text(name); + + expect(button, findsOneWidget); + lastFinder = button; + + await tester.tap(button); + await tester.pump(); + await tester.pump(); + await tester.pump(); + await tester.pump(); + await tester.pump(); + await tester.pump(); + await tester.pump(); + await tester.pump(); + var page = find.text('WebGenTag'); + try { + expect(page, findsOneWidget); + + await tester.pump(); + + await tester.fling(page, const Offset(0, -20000), 2); + try { + await tester.pumpAndSettle(); + await tester.pumpAndSettle(); + } catch (e) { + print('pumpAndSettle 2 error:$e'); + } + } catch (e) { + print("没有找到'WebGenTag',不用滑动,直接点击"); + } + var genBtn = find.text('生成Web使用md'); + expect(genBtn, findsOneWidget); + await tester.tap(genBtn); + + var back = find.text('返回首页'); + expect(back, findsOneWidget); + await tester.tap(back); + await tester.pumpAndSettle(); +} + diff --git a/example/web/favicon.png b/tdesign-component/example/web/favicon.png similarity index 100% rename from example/web/favicon.png rename to tdesign-component/example/web/favicon.png diff --git a/example/web/icons/Icon-192.png b/tdesign-component/example/web/icons/Icon-192.png similarity index 100% rename from example/web/icons/Icon-192.png rename to tdesign-component/example/web/icons/Icon-192.png diff --git a/example/web/icons/Icon-512.png b/tdesign-component/example/web/icons/Icon-512.png similarity index 100% rename from example/web/icons/Icon-512.png rename to tdesign-component/example/web/icons/Icon-512.png diff --git a/example/web/icons/Icon-maskable-192.png b/tdesign-component/example/web/icons/Icon-maskable-192.png similarity index 100% rename from example/web/icons/Icon-maskable-192.png rename to tdesign-component/example/web/icons/Icon-maskable-192.png diff --git a/example/web/icons/Icon-maskable-512.png b/tdesign-component/example/web/icons/Icon-maskable-512.png similarity index 100% rename from example/web/icons/Icon-maskable-512.png rename to tdesign-component/example/web/icons/Icon-maskable-512.png diff --git a/example/web/index.html b/tdesign-component/example/web/index.html similarity index 100% rename from example/web/index.html rename to tdesign-component/example/web/index.html diff --git a/example/web/manifest.json b/tdesign-component/example/web/manifest.json similarity index 100% rename from example/web/manifest.json rename to tdesign-component/example/web/manifest.json diff --git a/tdesign-component/feedback.png b/tdesign-component/feedback.png new file mode 100644 index 000000000..08d9e67cb Binary files /dev/null and b/tdesign-component/feedback.png differ diff --git a/tdesign-component/init.sh b/tdesign-component/init.sh new file mode 100644 index 000000000..cfab36e8b --- /dev/null +++ b/tdesign-component/init.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +# 可以指定自己的Flutter SDK路径 +#FLUTTER_SDK_PATH=~/tools/flutter + +# 设置基础目录(脚本所在目录)` +BASE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +# 检查Flutter SDK路径 +# 检查Flutter SDK路径 +if [[ -n "$FLUTTER_SDK_PATH" ]]; then + FLUTTER_CMD="$FLUTTER_SDK_PATH/bin/flutter" + echo "使用自定义Flutter路径: $FLUTTER_CMD" +else + FLUTTER_CMD="flutter" + echo "使用系统默认Flutter命令" +fi + +# 获取Flutter版本号(安全处理错误) +FLUTTER_VERSION=$($FLUTTER_CMD --version 2>/dev/null | awk '/Flutter/{print $2}') +if [[ -z "$FLUTTER_VERSION" ]]; then + echo "❌ 错误:无法获取Flutter版本号" >&2 + exit 1 +fi +echo "检测到Flutter版本: $FLUTTER_VERSION" + +# 版本比较函数(使用sort -V替代BASH_REMATCH) +version_ge() { + # 原理:通过版本排序后检查目标版本是否为最大值 + [[ "$(printf "%s\n" "$1" "3.32.0" | sort -V -r | head -n1)" == "$1" ]] +} + +# 执行对应版本的copy.sh +if version_ge "$FLUTTER_VERSION"; then + echo "✅ 版本 >= 3.32.0,执行高版本脚本" + COPY_SCRIPT="$BASE_DIR/example/shell/flutter_versions/3.32.2" +else + echo "ℹ️ 版本 < 3.32.0,执行低版本脚本" + COPY_SCRIPT="$BASE_DIR/example/shell/flutter_versions/3.16.9" +fi + +# 执行copy脚本 +if [[ -f "$COPY_SCRIPT/copy.sh" ]]; then + cd "$COPY_SCRIPT" + echo "执行: $COPY_SCRIPT" + bash "copy.sh" +else + echo "❌ 错误: 找不到脚本 $COPY_SCRIPT" >&2 + exit 1 +fi + +cd $BASE_DIR + +# 在基础目录执行清理和依赖安装 +echo "在 $BASE_DIR 执行清理操作..." +$FLUTTER_CMD clean + +rm $BASE_DIR/pubspec.lock +rm $BASE_DIR/example/pubspec.lock + +echo "获取项目依赖..." + +$FLUTTER_CMD pub get + +echo "✅ 所有操作完成" \ No newline at end of file diff --git a/tdesign-component/lib/src/components/action_sheet/td_action_sheet.dart b/tdesign-component/lib/src/components/action_sheet/td_action_sheet.dart new file mode 100644 index 000000000..e4e331851 --- /dev/null +++ b/tdesign-component/lib/src/components/action_sheet/td_action_sheet.dart @@ -0,0 +1,341 @@ +import 'package:flutter/material.dart'; + +import '../popup/td_popup_route.dart'; +import 'td_action_sheet.dart'; +import 'td_action_sheet_grid.dart'; +import 'td_action_sheet_group.dart'; +import 'td_action_sheet_list.dart'; + +export 'td_action_sheet_item.dart'; + +typedef TDActionSheetItemCallback = void Function(TDActionSheetItem item, int index); + +enum TDActionSheetTheme { list, grid, group } + +enum TDActionSheetAlign { center, left, right } + +/// 动作面板 +class TDActionSheet { + TDActionSheet( + this.context, { + this.align = TDActionSheetAlign.center, + this.cancelText = '取消', + this.count = 8, + this.rows = 2, + this.itemHeight = 96.0, + this.itemMinWidth = 80.0, + this.description, + required this.items, + this.showCancel = true, + this.showPagination = false, + this.scrollable = false, + this.theme = TDActionSheetTheme.list, + this.visible = false, + this.onCancel, + this.onClose, + this.onSelected, + this.showOverlay = true, + this.closeOnOverlayClick = true, + this.useSafeArea = true, + }) { + if (visible) { + show(); + } + } + + /// 上下文 + final BuildContext context; + + /// 对齐方式 + final TDActionSheetAlign align; + + /// 取消按钮的文本 + final String cancelText; + + /// 每页显示的项目数 + /// 当[theme]等于[TDActionSheetTheme.grid]且[showPagination]为true时有效 + final int count; + + /// 显示的行数 + /// 当[theme]等于[TDActionSheetTheme.grid]时有效 + final int rows; + + /// 项目的行高 + /// 当[theme]等于[TDActionSheetTheme.grid]或[theme]等于[TDActionSheetTheme.group]时有效 + final double itemHeight; + + /// 项目的最小宽度 + /// 当[theme]等于[TDActionSheetTheme.grid]且[scrollable]为true时有效 + /// 或当[theme]等于[TDActionSheetTheme.group]时有效 + final double itemMinWidth; + + /// 描述文本 + /// 当[theme]等于[TDActionSheetTheme.grid]或[theme]等于[TDActionSheetTheme.list]时有效 + final String? description; + + /// ActionSheet的项目列表 + final List items; + + /// 是否显示取消按钮 + final bool showCancel; + + /// 是否显示遮罩层 + final bool showOverlay; + + /// 点击蒙层时是否关闭 + final bool closeOnOverlayClick; + + /// 主题样式 + final TDActionSheetTheme theme; + + /// 是否立即显示 + final bool visible; + + /// 是否显示分页 + /// 当[theme]等于[TDActionSheetTheme.grid]时有效 + final bool showPagination; + + /// 是否可以横向滚动 + /// 当[theme]等于[TDActionSheetTheme.grid]且[showPagination]为false时有效 + final bool scrollable; + + /// 取消按钮的回调函数 + final VoidCallback? onCancel; + + /// 关闭时的回调函数 + final VoidCallback? onClose; + + /// 选择项目时的回调函数 + final TDActionSheetItemCallback? onSelected; + + /// 使用安全区域 + final bool useSafeArea; + + static TDSlidePopupRoute? _actionSheetRoute; + + /// 显示列表类型面板 + static void showListActionSheet( + BuildContext context, { + required List items, + TDActionSheetAlign align = TDActionSheetAlign.center, + String cancelText = '取消', + bool showCancel = true, + VoidCallback? onCancel, + TDActionSheetItemCallback? onSelected, + bool showOverlay = true, + bool closeOnOverlayClick = true, + VoidCallback? onClose, + bool useSafeArea = true, + }) { + _createRoute( + context, + theme: TDActionSheetTheme.list, + items: items, + align: align, + cancelText: cancelText, + showCancel: showCancel, + onSelected: onSelected, + onCancel: onCancel, + showOverlay: showOverlay, + closeOnOverlayClick: closeOnOverlayClick, + onClose: onClose, + useSafeArea: useSafeArea, + ); + } + + /// 显示宫格类型面板 + static void showGridActionSheet( + BuildContext context, { + required List items, + TDActionSheetAlign align = TDActionSheetAlign.center, + String cancelText = '取消', + bool showCancel = true, + TDActionSheetItemCallback? onSelected, + bool showOverlay = true, + bool closeOnOverlayClick = true, + int count = 8, + int rows = 2, + double itemHeight = 96.0, + double itemMinWidth = 80.0, + bool scrollable = false, + bool showPagination = false, + VoidCallback? onCancel, + String? description, + VoidCallback? onClose, + bool useSafeArea = true, + }) { + _createRoute( + context, + theme: TDActionSheetTheme.grid, + items: items, + align: align, + cancelText: cancelText, + showCancel: showCancel, + onSelected: onSelected, + onCancel: onCancel, + showOverlay: showOverlay, + closeOnOverlayClick: closeOnOverlayClick, + count: count, + rows: rows, + itemHeight: itemHeight, + itemMinWidth: itemMinWidth, + scrollable: scrollable, + showPagination: showPagination, + description: description, + onClose: onClose, + useSafeArea: useSafeArea, + ); + } + + /// 显示分组类型面板 + static void showGroupActionSheet( + BuildContext context, { + required List items, + TDActionSheetAlign align = TDActionSheetAlign.left, + String cancelText = '取消', + bool showCancel = true, + TDActionSheetItemCallback? onSelected, + bool showOverlay = true, + bool closeOnOverlayClick = true, + double itemHeight = 96.0, + double itemMinWidth = 80.0, + VoidCallback? onCancel, + VoidCallback? onClose, + bool useSafeArea = true, + }) { + _createRoute( + context, + theme: TDActionSheetTheme.group, + items: items, + align: align, + cancelText: cancelText, + showCancel: showCancel, + onSelected: onSelected, + onCancel: onCancel, + showOverlay: showOverlay, + closeOnOverlayClick: closeOnOverlayClick, + itemHeight: itemHeight, + itemMinWidth: itemMinWidth, + onClose: onClose, + useSafeArea: useSafeArea, + ); + } + + /// 显示动作面板 + void show() { + TDActionSheet._createRoute( + context, + theme: theme, + items: items, + align: align, + cancelText: cancelText, + showCancel: showCancel, + onSelected: onSelected, + onCancel: onCancel, + showOverlay: showOverlay, + closeOnOverlayClick: closeOnOverlayClick, + count: count, + rows: rows, + itemHeight: itemHeight, + itemMinWidth: itemMinWidth, + scrollable: scrollable, + showPagination: showPagination, + description: description, + onClose: onClose, + useSafeArea: useSafeArea, + ); + } + + void open() { + show(); + } + + @mustCallSuper + void close() { + if (_actionSheetRoute != null) { + Navigator.of(context).pop(); + } + } + + /// 创建路由 + static void _createRoute( + BuildContext context, { + required TDActionSheetTheme theme, + required List items, + TDActionSheetAlign align = TDActionSheetAlign.center, + String cancelText = '取消', + bool showCancel = true, + TDActionSheetItemCallback? onSelected, + bool showOverlay = true, + bool closeOnOverlayClick = true, + int count = 8, + int rows = 2, + double itemHeight = 96.0, + double itemMinWidth = 80.0, + bool scrollable = false, + bool showPagination = false, + VoidCallback? onCancel, + String? description, + VoidCallback? onClose, + bool useSafeArea = true, + }) { + if (_actionSheetRoute != null) { + return; + } + _actionSheetRoute = TDSlidePopupRoute( + slideTransitionFrom: SlideTransitionFrom.bottom, + isDismissible: showOverlay ? closeOnOverlayClick : false, + modalBarrierColor: showOverlay ? null : Colors.transparent, + builder: (context) { + switch (theme) { + case TDActionSheetTheme.list: + return TDActionSheetList( + items: items, + align: align, + cancelText: cancelText, + description: description, + showCancel: showCancel, + onCancel: onCancel, + onSelected: onSelected, + useSafeArea: useSafeArea, + ); + case TDActionSheetTheme.grid: + return TDActionSheetGrid( + items: items, + align: align, + onSelected: onSelected, + showCancel: showCancel, + showPagination: showPagination, + scrollable: scrollable, + cancelText: cancelText, + description: description, + count: count, + rows: rows, + onCancel: onCancel, + itemHeight: itemHeight, + itemMinWidth: itemMinWidth, + useSafeArea: useSafeArea, + ); + case TDActionSheetTheme.group: + return TDActionSheetGroup( + items: items, + align: align, + cancelText: cancelText, + showCancel: showCancel, + onCancel: onCancel, + onSelected: onSelected, + itemHeight: itemHeight, + itemMinWidth: itemMinWidth, + useSafeArea: useSafeArea, + ); + default: + return const SizedBox.shrink(); + } + }, + ); + Navigator.of(context).push(_actionSheetRoute!).then((_) { + _actionSheetRoute = null; + onClose?.call(); + }); + } +} diff --git a/tdesign-component/lib/src/components/action_sheet/td_action_sheet_grid.dart b/tdesign-component/lib/src/components/action_sheet/td_action_sheet_grid.dart new file mode 100644 index 000000000..b3379e4a2 --- /dev/null +++ b/tdesign-component/lib/src/components/action_sheet/td_action_sheet_grid.dart @@ -0,0 +1,211 @@ +import 'package:flutter/material.dart'; +import '../../theme/td_colors.dart'; +import '../../theme/td_fonts.dart'; +import '../../theme/td_radius.dart'; +import '../../theme/td_spacers.dart'; +import '../../theme/td_theme.dart'; +import '../../util/iterable_ext.dart'; +import '../../util/list_ext.dart'; +import '../badge/td_badge.dart'; +import '../text/td_text.dart'; +import 'td_action_sheet.dart'; +import 'td_action_sheet_item_widget.dart'; + +class TDActionSheetGrid extends StatefulWidget { + final List items; + final String? description; + final TDActionSheetAlign align; + final int count; + final int rows; + final String cancelText; + final bool showCancel; + final bool showPagination; + final bool scrollable; + final VoidCallback? onCancel; + final TDActionSheetItemCallback? onSelected; + final double itemHeight; + final double itemMinWidth; + final bool useSafeArea; + + const TDActionSheetGrid({ + super.key, + required this.items, + this.description, + this.align = TDActionSheetAlign.center, + this.count = 8, + this.rows = 2, + this.cancelText = '取消', + this.showCancel = true, + this.showPagination = false, + this.scrollable = false, + this.onCancel, + this.onSelected, + this.itemHeight = 96.0, + this.itemMinWidth = 80.0, + this.useSafeArea = true, + }); + + @override + _TDActionSheetGridState createState() => _TDActionSheetGridState(); +} + +class _TDActionSheetGridState extends State { + int currentPage = 0; + + @override + Widget build(BuildContext context) { + final borderRadius = Radius.circular(TDTheme.of(context).radiusExtraLarge); + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only(topLeft: borderRadius, topRight: borderRadius), + color: TDTheme.of(context).whiteColor1, + ), + clipBehavior: Clip.antiAlias, + padding: widget.useSafeArea ? EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom) : EdgeInsets.zero, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: TDTheme.of(context).spacer8), + // 如果有描述,则显示描述 + if (widget.description != null) _buildDescription(context), + // 如果显示分页,则显示分页点 + if (widget.showPagination) ...[ + _buildPaginationGrid(context), + _buildPaginationDots(context), + // 横向滚动 + ] else if (widget.scrollable) + _buildScrollGrid(context) + else + _buildGrid(context), + // 如果显示取消按钮,则显示取消按钮 + if (widget.showCancel) buildCancelButton(context, widget.showPagination, widget.cancelText, widget.onCancel), + ], + ), + ); + } + + Widget _buildDescription(BuildContext context) { + return Padding( + padding: EdgeInsets.only( + left: TDTheme.of(context).spacer16, + right: TDTheme.of(context).spacer16, + top: TDTheme.of(context).spacer4, + ), + child: Row( + mainAxisAlignment: getMainAxisAlignment(widget.align), + children: [ + TDText( + widget.description!, + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor3, + ), + ], + ), + ); + } + + Widget _gridWrap(Widget child) { + return SizedBox( + height: widget.rows * widget.itemHeight, + child: child, + ); + } + + Widget _buildPaginationGrid(BuildContext context) { + return _gridWrap( + PageView.builder( + itemCount: (widget.items.length / widget.count).ceil(), + // 当页面改变时更新当前页码 + onPageChanged: widget.showPagination + ? (index) { + setState(() { + currentPage = index; + }); + } + : null, + itemBuilder: (context, pageIndex) { + // 获取当前页面的项目 + final pageItems = widget.items.skip(pageIndex * widget.count).take(widget.count).toList(); + return _buildGrid(context, items: pageItems, pageIndex: pageIndex); + }, + ), + ); + } + + Widget _buildScrollGrid(BuildContext context) { + final chunks = widget.items.chunk((widget.items.length / widget.rows).ceil()); + final itemCount = chunks[0].length; + return _gridWrap( + ListView.builder( + itemCount: itemCount, + padding: EdgeInsets.zero, + scrollDirection: Axis.horizontal, + itemBuilder: (context, col) { + return Column( + children: List.generate(chunks.length, (row) { + final index = itemCount * row + col; + return SizedBox( + width: widget.itemMinWidth, + height: widget.itemHeight, + child: TDActionSheetItemWidget( + item: chunks[row].getOrNull(col), + index: index, + onSelected: widget.onSelected, + ), + ); + }), + ); + }, + ), + ); + } + + Widget _buildGrid( + BuildContext context, { + List? items, + int pageIndex = 0, + }) { + // 计算每行的项目数 + final itemsPerRow = widget.count ~/ widget.rows; + // 获取屏幕宽度 + final screenWidth = MediaQuery.of(context).size.width; + // 计算子项的宽高比 + final childAspectRatio = screenWidth / itemsPerRow / widget.itemHeight; + return _gridWrap( + GridView.builder( + physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + itemCount: (items ?? widget.items).length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: itemsPerRow, + childAspectRatio: childAspectRatio, + ), + itemBuilder: (context, index) { + final item = (items ?? widget.items)[index]; + return TDActionSheetItemWidget( + item: item, + index: pageIndex * widget.count + index, + onSelected: widget.onSelected, + ); + }, + ), + ); + } + + Widget _buildPaginationDots(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate((widget.items.length / widget.count).ceil(), (index) { + return Container( + margin: EdgeInsets.symmetric(horizontal: TDTheme.of(context).spacer4), + width: 8.0, + height: 8.0, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: currentPage == index ? TDTheme.of(context).brandColor7 : TDTheme.of(context).grayColor4, + ), + ); + }), + ); + } +} diff --git a/tdesign-component/lib/src/components/action_sheet/td_action_sheet_group.dart b/tdesign-component/lib/src/components/action_sheet/td_action_sheet_group.dart new file mode 100644 index 000000000..72c0c72ab --- /dev/null +++ b/tdesign-component/lib/src/components/action_sheet/td_action_sheet_group.dart @@ -0,0 +1,115 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/widgets.dart'; + +import '../../theme/td_colors.dart'; +import '../../theme/td_fonts.dart'; +import '../../theme/td_radius.dart'; +import '../../theme/td_spacers.dart'; +import '../../theme/td_theme.dart'; +import '../../util/list_ext.dart'; +import '../text/td_text.dart'; +import 'td_action_sheet.dart'; +import 'td_action_sheet_item_widget.dart'; + +class TDActionSheetGroup extends StatelessWidget { + final List items; + final TDActionSheetAlign align; + final String cancelText; + final bool showCancel; + final VoidCallback? onCancel; + final TDActionSheetItemCallback? onSelected; + final double itemHeight; + final double itemMinWidth; + final bool useSafeArea; + + const TDActionSheetGroup({ + super.key, + required this.items, + this.align = TDActionSheetAlign.left, + this.cancelText = '取消', + this.showCancel = true, + this.onCancel, + this.onSelected, + this.itemHeight = 96.0, + this.itemMinWidth = 80.0, + this.useSafeArea = true, + }); + + @override + Widget build(BuildContext context) { + final borderRadius = Radius.circular(TDTheme.of(context).radiusExtraLarge); + final groupItems = items.groupBy((item) => item.group); + final groupKeys = groupItems.keys.where((k) => k != null && groupItems[k]?.isNotEmpty == true); + + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only(topLeft: borderRadius, topRight: borderRadius), + color: TDTheme.of(context).whiteColor1, + ), + clipBehavior: Clip.antiAlias, + padding: useSafeArea ? EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom) : EdgeInsets.zero, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ...groupKeys.mapIndexed((i, k) { + final list = groupItems[k]!; + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.fromLTRB( + TDTheme.of(context).spacer16, + TDTheme.of(context).spacer12, + TDTheme.of(context).spacer16, + 0, + ), + child: Row( + mainAxisAlignment: getMainAxisAlignment(align), + children: [ + TDText( + k!, + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor3, + ), + ], + ), + ), + SizedBox( + height: itemHeight, + child: ListView.builder( + itemCount: list.length, + padding: EdgeInsets.zero, + scrollDirection: Axis.horizontal, + itemBuilder: (context, row) { + return SizedBox( + width: itemMinWidth, + child: TDActionSheetItemWidget( + item: list[row], + onSelected: onSelected, + index: items.indexOf(list[row]), + ), + ); + }, + ), + ), + if (i != groupKeys.length - 1) + Container( + decoration: BoxDecoration( + color: TDTheme.of(context).fontWhColor1, + border: Border( + top: BorderSide( + color: TDTheme.of(context).grayColor3, + width: 0.5, + ), + ), + ), + ), + ], + ); + }), + if (showCancel) buildCancelButton(context, false, cancelText, onCancel), + ], + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/action_sheet/td_action_sheet_item.dart b/tdesign-component/lib/src/components/action_sheet/td_action_sheet_item.dart new file mode 100644 index 000000000..7e8437d00 --- /dev/null +++ b/tdesign-component/lib/src/components/action_sheet/td_action_sheet_item.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + +import '../badge/td_badge.dart'; +import 'td_action_sheet.dart'; + +/// 动作面板项目 +class TDActionSheetItem { + TDActionSheetItem({ + required this.label, + this.textStyle, + this.icon, + this.badge, + this.disabled = false, + this.iconSize, + this.group, + }); + + /// 标提 + final String label; + + /// 标题样式 + final TextStyle? textStyle; + + /// 图标 + final Widget? icon; + + /// 角标 + final TDBadge? badge; + + /// 是否禁用 + final bool disabled; + + /// 图标大小 + final double? iconSize; + + /// 分组,用于带描述多行滚动宫格 + /// 当[TDActionSheet.theme]等于[TDActionSheetTheme.group]时有效 + /// 有效时,如果该值未配置整个[TDActionSheetItem]会被忽略,即不会展示 + final String? group; +} + diff --git a/tdesign-component/lib/src/components/action_sheet/td_action_sheet_item_widget.dart b/tdesign-component/lib/src/components/action_sheet/td_action_sheet_item_widget.dart new file mode 100644 index 000000000..7fe75a170 --- /dev/null +++ b/tdesign-component/lib/src/components/action_sheet/td_action_sheet_item_widget.dart @@ -0,0 +1,138 @@ +import 'package:flutter/material.dart'; +import '../../theme/td_colors.dart'; +import '../../theme/td_fonts.dart'; +import '../../theme/td_spacers.dart'; +import '../../theme/td_theme.dart'; +import '../badge/td_badge.dart'; +import '../text/td_text.dart'; +import 'td_action_sheet.dart'; + +class TDActionSheetItemWidget extends StatelessWidget { + const TDActionSheetItemWidget({ + super.key, + this.item, + required this.index, + this.onSelected, + }); + + final TDActionSheetItem? item; + final int index; + final TDActionSheetItemCallback? onSelected; + + @override + Widget build(BuildContext context) { + if (item == null) { + return const SizedBox.shrink(); + } + late ValueNotifier> _offsetValue; + late GlobalKey _offsetKey; + if (item!.badge != null) { + _offsetValue = ValueNotifier(const [0.0, 0.0]); + _offsetKey = GlobalKey(); + } + return GestureDetector( + onTap: item!.disabled + ? null + : () { + onSelected?.call(item!, index); + Navigator.maybePop(context); + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (item!.icon != null) ...[ + Stack( + clipBehavior: Clip.none, + children: [ + SizedBox( + width: item!.iconSize ?? 40, + height: item!.iconSize ?? 40, + child: FittedBox( + fit: BoxFit.contain, + child: item!.icon!, + ), + ), + if (item!.badge != null) + ValueListenableBuilder( + valueListenable: _offsetValue, + builder: (context, value, child) { + _setOffsetValue(_offsetKey, _offsetValue); + return Positioned( + key: _offsetKey, + child: item!.badge!, + right: value[0], + top: value[1], + ); + }, + ), + ], + ), + SizedBox(height: TDTheme.of(context).spacer8), + ], + TDText( + item!.label, + font: TDTheme.of(context).fontBodySmall, + textColor: TDTheme.of(context).fontGyColor1, + style: item!.textStyle, + ), + ], + ), + ); + } + + void _setOffsetValue(GlobalKey> offsetKey, ValueNotifier> offsetValue) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + final renderBox = offsetKey.currentContext?.findRenderObject() as RenderBox; + final size = renderBox.size; + final right = -size.width / 2; + final top = -size.height / 2; + if (offsetValue.value[0] != right || offsetValue.value[1] != top) { + offsetValue.value = [right, top]; + } + }); + } +} + +/// 获取主轴对齐方式 +MainAxisAlignment getMainAxisAlignment(TDActionSheetAlign align) { + switch (align) { + case TDActionSheetAlign.left: + return MainAxisAlignment.start; + case TDActionSheetAlign.right: + return MainAxisAlignment.end; + case TDActionSheetAlign.center: + default: + return MainAxisAlignment.center; + } +} + +Widget buildCancelButton(BuildContext context, bool showPagination, String cancelText, VoidCallback? onCancel) { + return Padding( + padding: EdgeInsets.only(top: showPagination ? TDTheme.of(context).spacer16 : TDTheme.of(context).spacer8), + child: GestureDetector( + onTap: () { + onCancel?.call(); + Navigator.maybePop(context); + }, + child: Container( + decoration: BoxDecoration( + color: TDTheme.of(context).fontWhColor1, + border: Border( + top: BorderSide( + color: TDTheme.of(context).grayColor3, + width: 0.5, + ), + ), + ), + height: 48, + child: Center( + child: TDText( + cancelText, + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).fontGyColor1, + ), + ), + ), + ), + ); +} diff --git a/tdesign-component/lib/src/components/action_sheet/td_action_sheet_list.dart b/tdesign-component/lib/src/components/action_sheet/td_action_sheet_list.dart new file mode 100644 index 000000000..2db22af68 --- /dev/null +++ b/tdesign-component/lib/src/components/action_sheet/td_action_sheet_list.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import '../../theme/td_colors.dart'; +import '../../theme/td_fonts.dart'; +import '../../theme/td_radius.dart'; +import '../../theme/td_spacers.dart'; +import '../../theme/td_theme.dart'; +import '../badge/td_badge.dart'; +import '../text/td_text.dart'; +import 'td_action_sheet.dart'; +import 'td_action_sheet_item_widget.dart'; + +class TDActionSheetList extends StatelessWidget { + final List items; + final TDActionSheetAlign align; + final String cancelText; + final String? description; + final bool showCancel; + final VoidCallback? onCancel; + final TDActionSheetItemCallback? onSelected; + final bool useSafeArea; + + const TDActionSheetList({ + super.key, + required this.items, + this.align = TDActionSheetAlign.center, + this.cancelText = '取消', + this.description, + this.showCancel = true, + this.onCancel, + this.onSelected, + this.useSafeArea = true, + }); + + @override + Widget build(BuildContext context) { + final borderRadius = Radius.circular(TDTheme.of(context).radiusExtraLarge); + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only(topLeft: borderRadius, topRight: borderRadius), + color: TDTheme.of(context).grayColor1, + ), + clipBehavior: Clip.antiAlias, + padding: useSafeArea ? EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom) : EdgeInsets.zero, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (description != null) _buildDescription(context), + _buildOptionsList(context), + if (showCancel) _buildCancelButton(context), + ], + ), + ); + } + + /// 构建描述文本 + Widget _buildDescription(BuildContext context) { + return Container( + padding: EdgeInsets.symmetric( + horizontal: TDTheme.of(context).spacer16, + vertical: TDTheme.of(context).spacer12, + ), + decoration: BoxDecoration( + color: TDTheme.of(context).fontWhColor1, + border: Border( + bottom: BorderSide( + color: TDTheme.of(context).grayColor1, + width: 0.5, + ), + ), + ), + child: Row( + mainAxisAlignment: getMainAxisAlignment(align), + children: [ + TDText( + description!, + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor3, + ), + ], + ), + ); + } + + /// 构建选项列表 + Widget _buildOptionsList(BuildContext context) { + return Container( + color: TDTheme.of(context).fontWhColor1, + child: ListView.builder( + shrinkWrap: true, + itemCount: items.length, + padding: EdgeInsets.zero, + itemBuilder: (context, index) { + final item = items[index]; + return GestureDetector( + onTap: item.disabled + ? null // 如果项被禁用,则不设置点击事件 + : () { + onSelected?.call(item, index); // 触发选中回调 + Navigator.maybePop(context); // 关闭当前页面 + }, + child: Container( + height: 56, + padding: EdgeInsets.symmetric(horizontal: TDTheme.of(context).spacer16), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: TDTheme.of(context).grayColor1, + width: 0.5, + ), + ), + ), + child: Row( + mainAxisAlignment: getMainAxisAlignment(align), + children: [ + if (item.icon != null) ...[ + IconTheme( + data: IconThemeData( + color: item.disabled + ? TDTheme.of(context).fontGyColor4 // 禁用状态下的图标颜色 + : (item.textStyle?.color ?? TDTheme.of(context).fontGyColor1), // 正常状态下的图标颜色 + size: item.textStyle?.fontSize, + ), + child: SizedBox( + width: item.iconSize ?? 24, + height: item.iconSize ?? 24, + child: item.icon!, + ), + ), + SizedBox(width: TDTheme.of(context).spacer8), + ], + TDText( + item.label, + font: TDTheme.of(context).fontBodyLarge, + textColor: item.disabled + ? TDTheme.of(context).fontGyColor4 // 禁用状态下的文本颜色 + : TDTheme.of(context).fontGyColor1, // 正常状态下的文本颜色 + style: item.textStyle, + ), + if (item.badge != null) ...[ + SizedBox(width: TDTheme.of(context).spacer8), + item.badge!, + ], + ], + ), + ), + ); + }, + ), + ); + } + + /// 构建取消按钮 + Widget _buildCancelButton(BuildContext context) { + return Column( + children: [ + SizedBox(height: TDTheme.of(context).spacer8), + GestureDetector( + onTap: () { + onCancel?.call(); + Navigator.maybePop(context); + }, + child: Container( + color: TDTheme.of(context).fontWhColor1, + height: 48, + child: Center( + child: TDText( + cancelText, + font: TDTheme.of(context).fontBodyLarge, + textColor: TDTheme.of(context).fontGyColor1, + ), + ), + ), + ), + ], + ); + } +} diff --git a/tdesign-component/lib/src/components/avatar/td_avatar.dart b/tdesign-component/lib/src/components/avatar/td_avatar.dart new file mode 100644 index 000000000..5997f7008 --- /dev/null +++ b/tdesign-component/lib/src/components/avatar/td_avatar.dart @@ -0,0 +1,432 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +enum TDAvatarSize { large, medium, small } + +enum TDAvatarType { + icon, + normal, + customText, + display, + operation +} + +enum TDAvatarShape { + circle, + square +} + +/// 用于头像显示 +class TDAvatar extends StatelessWidget { + const TDAvatar({ + Key? key, + this.size = TDAvatarSize.medium, + this.type = TDAvatarType.normal, + this.shape = TDAvatarShape.circle, + this.text, + this.radius, + this.icon, + this.avatarUrl, + this.avatarSize, + this.avatarDisplayList, + this.displayText, + this.onTap, + this.defaultUrl = '', + this.avatarDisplayWidget, + this.avatarDisplayBorder = 2, + this.avatarDisplayListAsset, + this.backgroundColor, + this.fit, + }) : super(key: key); + + /// 头像地址 + final String? avatarUrl; + + /// 头像尺寸 + final TDAvatarSize size; + + /// 头像类型 + final TDAvatarType type; + + /// 头像形状 + final TDAvatarShape shape; + + /// 自定义文字 + final String? text; + + /// 自定义圆角 + final double? radius; + + /// 自定义头像大小 + final double? avatarSize; + + /// 自定义图标 + final IconData? icon; + + /// 默认图片(本地) + final String defaultUrl; + + /// 带操作\展示的头像列表 + final List? avatarDisplayList; + + /// 带操作\展示的头像列表 (本地资源) + final List? avatarDisplayListAsset; + + /// 带操作\展示的头像描边宽度 + final double avatarDisplayBorder; + + /// 带操作头像自定义操作Widget + final Widget? avatarDisplayWidget; + + /// 纯展示类型末尾文字 + final String? displayText; + + /// 操作点击事件 + final Function()? onTap; + + /// 自定义文案时背景色 + final Color? backgroundColor; + + /// 自定义图片对齐方式 + final BoxFit? fit; + + + double _getAvatarWidth() { + double width; + switch (size) { + case TDAvatarSize.large: + width = 64; + break; + case TDAvatarSize.medium: + width = 48; + break; + case TDAvatarSize.small: + width = 40; + break; + } + return avatarSize ?? width; + } + + Font? _getTextFont(BuildContext context) { + Font? font; + switch (size) { + case TDAvatarSize.large: + font = TDTheme.of(context).fontTitleExtraLarge; + break; + case TDAvatarSize.medium: + font = TDTheme.of(context).fontTitleMedium; + break; + case TDAvatarSize.small: + font = TDTheme.of(context).fontTitleSmall; + break; + } + return font; + } + + double _getIconWidth() { + double width; + switch (size) { + case TDAvatarSize.large: + width = 32; + break; + case TDAvatarSize.medium: + width = 24; + break; + case TDAvatarSize.small: + width = 20; + break; + } + return width; + } + + double _getAvatarRadius(BuildContext context) { + double _radius; + switch (shape) { + case TDAvatarShape.circle: + _radius = _getAvatarWidth() / 2; + break; + case TDAvatarShape.square: + _radius = TDTheme.of(context).radiusDefault; + break; + } + return radius ?? _radius; + } + + @override + Widget build(BuildContext context) { + switch (type) { + case TDAvatarType.icon: + return GestureDetector( + child: Container( + width: _getAvatarWidth(), + height: _getAvatarWidth(), + decoration: BoxDecoration( + color: backgroundColor ?? TDTheme.of(context).brandColor2, + borderRadius: BorderRadius.circular(_getAvatarRadius(context)), + ), + child: Center( + child: Icon(icon ?? TDIcons.user, size: _getIconWidth(), color: TDTheme.of(context).brandNormalColor)), + ), + onTap: onTap, + ); + case TDAvatarType.normal: + return GestureDetector( + child: Container( + width: _getAvatarWidth(), + height: _getAvatarWidth(), + decoration: BoxDecoration( + color: backgroundColor ?? TDTheme.of(context).brandColor2, + borderRadius: BorderRadius.circular(_getAvatarRadius(context)), + image: avatarUrl != null + ? DecorationImage(image: NetworkImage(avatarUrl!)) + : defaultUrl != '' + ? DecorationImage(image: AssetImage(defaultUrl)) + : null), + ), + onTap: onTap, + ); + case TDAvatarType.customText: + return GestureDetector( + child: Container( + width: _getAvatarWidth(), + height: _getAvatarWidth(), + decoration: BoxDecoration( + color: backgroundColor ?? TDTheme.of(context).brandNormalColor, + borderRadius: BorderRadius.circular(_getAvatarRadius(context)), + ), + child: Center( + child: TDText( + text, + forceVerticalCenter: true, + textAlign: TextAlign.center, + font: _getTextFont(context), + textColor: TDTheme.of(context).whiteColor1, + ), + ), + ), + onTap: onTap, + ); + case TDAvatarType.display: + return buildDisplayAvatar(context); + case TDAvatarType.operation: + return buildOperationAvatar(context); + } + } + + double _getDisplayPadding() { + double padding; + switch (size) { + case TDAvatarSize.large: + padding = 10; + break; + case TDAvatarSize.medium: + padding = 8; + break; + case TDAvatarSize.small: + padding = 6; + break; + } + return padding; + } + + Widget buildOperationAvatar(BuildContext context) { + var list = []; + if ((avatarDisplayList == null || avatarDisplayList!.isEmpty) && + (avatarDisplayListAsset == null || avatarDisplayListAsset!.isEmpty)) { + return Container(); + } + + var length = 0; + + if (avatarDisplayList != null) { + length = avatarDisplayList!.length; + for (var i = 0; i < avatarDisplayList!.length + 1; i++) { + var left = (_getAvatarWidth() - _getDisplayPadding()) * i; + if (i == avatarDisplayList!.length) { + list.add(Positioned( + left: left, + child: GestureDetector( + onTap: () { + if (onTap != null) { + onTap!(); + } + }, + child: Container( + child: Center( + child: Icon(TDIcons.user_add, size: _getIconWidth(), color: TDTheme.of(context).brandNormalColor), + ), + width: _getAvatarWidth(), + height: _getAvatarWidth(), + clipBehavior: Clip.hardEdge, + decoration: ShapeDecoration( + color: TDTheme.of(context).brandColor2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), + side: BorderSide(color: Colors.white, width: avatarDisplayBorder)), + )), + ))); + } else { + list.add(Positioned( + left: left, + child: Container( + width: _getAvatarWidth(), + height: _getAvatarWidth(), + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), + side: BorderSide(color: Colors.white, width: avatarDisplayBorder)), + image: DecorationImage(image: NetworkImage(avatarDisplayList![i]), fit:fit??BoxFit.cover))))); + } + } + } else if (avatarDisplayListAsset != null) { + length = avatarDisplayListAsset!.length; + for (var i = 0; i < avatarDisplayListAsset!.length + 1; i++) { + var left = (_getAvatarWidth() - _getDisplayPadding()) * i; + if (i == avatarDisplayListAsset!.length) { + list.add(Positioned( + left: left, + child: GestureDetector( + onTap: () { + if (onTap != null) { + onTap!(); + } + }, + child: Container( + child: Center( + child: avatarDisplayWidget ?? + Icon(TDIcons.user_add, size: _getIconWidth(), color: TDTheme.of(context).brandNormalColor), + ), + width: _getAvatarWidth(), + height: _getAvatarWidth(), + clipBehavior: Clip.hardEdge, + decoration: ShapeDecoration( + color: TDTheme.of(context).brandColor2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), + side: BorderSide(color: Colors.white, width: avatarDisplayBorder)), + )), + ))); + } else { + list.add(Positioned( + left: left, + child: Container( + width: _getAvatarWidth(), + height: _getAvatarWidth(), + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), + side: BorderSide(color: Colors.white, width: avatarDisplayBorder)), + image: DecorationImage(image: AssetImage(avatarDisplayListAsset![i]), fit:fit?? BoxFit.fill))))); + } + } + } + + return SizedBox( + height: _getAvatarWidth(), + width: _getAvatarWidth() * (length + 1) - length * _getDisplayPadding(), + child: Stack(children: list), + ); + } + + Widget buildDisplayAvatar(BuildContext context) { + var list = []; + if ((avatarDisplayList == null || avatarDisplayList!.isEmpty) && + (avatarDisplayListAsset == null || avatarDisplayListAsset!.isEmpty)) { + return Container(); + } + + var length = 0; + + if (avatarDisplayList != null) { + length = avatarDisplayList!.length; + for (var i = avatarDisplayList!.length; i >= 0; i--) { + var left = (_getAvatarWidth() - _getDisplayPadding()) * i; + if (i == avatarDisplayList!.length) { + list.add(Positioned( + left: left, + child: Container( + child: Center( + child: TDText( + displayText, + fontWeight: FontWeight.w600, + forceVerticalCenter: true, + textAlign: TextAlign.center, + font: _getTextFont(context), + textColor: TDTheme.of(context).brandNormalColor, + ), + ), + width: _getAvatarWidth(), + height: _getAvatarWidth(), + clipBehavior: Clip.hardEdge, + decoration: ShapeDecoration( + color: TDTheme.of(context).brandColor2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), + side: BorderSide(color: Colors.white, width: avatarDisplayBorder)), + )))); + } else { + list.add(Positioned( + left: left, + child: Container( + width: _getAvatarWidth(), + height: _getAvatarWidth(), + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), + side: BorderSide(color: Colors.white, width: avatarDisplayBorder)), + image: DecorationImage(image: NetworkImage(avatarDisplayList![i]), fit:fit??BoxFit.cover))))); + } + } + } else if (avatarDisplayListAsset != null) { + length = avatarDisplayListAsset!.length; + for (var i = avatarDisplayListAsset!.length; i >= 0; i--) { + var left = (_getAvatarWidth() - _getDisplayPadding()) * i; + if (i == avatarDisplayListAsset!.length) { + list.add(Positioned( + left: left, + child: Container( + child: Center( + child: TDText( + displayText, + fontWeight: FontWeight.w600, + forceVerticalCenter: true, + textAlign: TextAlign.center, + font: _getTextFont(context), + textColor: TDTheme.of(context).brandNormalColor, + ), + ), + width: _getAvatarWidth(), + height: _getAvatarWidth(), + clipBehavior: Clip.hardEdge, + decoration: ShapeDecoration( + color: TDTheme.of(context).brandColor2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), + side: BorderSide(color: Colors.white, width: avatarDisplayBorder)), + )))); + } else { + list.add(Positioned( + left: left, + child: Container( + width: _getAvatarWidth(), + height: _getAvatarWidth(), + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(_getAvatarWidth() - _getDisplayPadding()), + side: BorderSide(color: Colors.white, width: avatarDisplayBorder)), + image: DecorationImage(image: AssetImage(avatarDisplayListAsset![i]), fit:fit??BoxFit.cover))))); + } + } + } + + return SizedBox( + height: _getAvatarWidth(), + width: _getAvatarWidth() * (length + 1) - length * _getDisplayPadding(), + child: Stack(children: list), + ); + } +} diff --git a/tdesign-component/lib/src/components/backtop/td_backtop.dart b/tdesign-component/lib/src/components/backtop/td_backtop.dart new file mode 100644 index 000000000..875651b7f --- /dev/null +++ b/tdesign-component/lib/src/components/backtop/td_backtop.dart @@ -0,0 +1,170 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; + + +enum TDBackTopTheme { + light, // 明亮主题 + dark // 暗黑主题 +} + +enum TDBackTopStyle { + circle, // 圆形 + halfCircle, // 半圆形 +} + +class TDBackTop extends StatefulWidget { + const TDBackTop({ + Key? key, + this.controller, + this.theme = TDBackTopTheme.light, + this.style = TDBackTopStyle.circle, + this.showText = false, + this.onClick, + }) : super(key: key); + + /// 页面滚动的控制器 + final ScrollController? controller; + + /// 主题 + final TDBackTopTheme theme; + + /// 样式,圆形和半圆 + final TDBackTopStyle style; + + /// 是否展示文字 + final bool showText; + + /// 按钮点击事件 + final VoidCallback? onClick; + + @override + State createState() => _TDBackTopState(); +} + +class _TDBackTopState extends State { + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + if (widget.controller != null && widget.controller!.hasClients) { + widget.controller!.animateTo(0, + duration: const Duration(milliseconds: 500), + curve: Curves.easeIn); + } + + if (widget.onClick != null) { + widget.onClick!(); + } + }, + child: widget.style == TDBackTopStyle.circle + ? _buildCircleWidget(context) + : _buildHalfCircleWidget(context), + ); + } + + Widget _buildCircleWidget(BuildContext context) { + var color = widget.theme == TDBackTopTheme.dark + ? Colors.white + : const Color.fromRGBO(0, 0, 0, 0.9); + + return Container( + width: 48, + height: 48, + padding: EdgeInsets.symmetric( + vertical: widget.showText ? 6 : 13), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(999), + border:Border.all(color: widget.theme== TDBackTopTheme.dark? TDTheme.of(context).grayColor14 :TDTheme.of(context).grayColor4,width: 0.5), + color: widget.theme == TDBackTopTheme.light + ? Colors.white + : TDTheme.of(context).grayColor14), + + child: Center( + child: Column( + children: [ + Icon( + TDIcons.backtop, + size: 20, + color: color, + ), + Visibility( + visible: widget.showText, + child: TDText( + context.resource.top, + maxLines: 1, + overflow: TextOverflow.visible, + style: TextStyle( + fontSize: 10, color: color, fontWeight: FontWeight.w600), + ), + ) + ], + )), + ); + } + + Widget _buildHalfCircleWidget(BuildContext context) { + var color = widget.theme == TDBackTopTheme.dark + ? Colors.white + : const Color.fromRGBO(0, 0, 0, 0.9); + + return ConstrainedBox( + constraints: const BoxConstraints(minWidth: 38), + child: Container( + height: 40, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: widget.theme == TDBackTopTheme.light + ? Colors.white + : TDTheme.of(context).grayColor14, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(999), + bottomLeft: Radius.circular(999) + ), + border:Border.all(color: widget.theme== TDBackTopTheme.dark?Color.fromRGBO(94, 94, 94, 1):Color.fromRGBO(220, 220, 220, 1),width: 0.5) + ), + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + TDIcons.backtop, + size: 22, + color: color, + ), + const SizedBox( + width: 2, + ), + Visibility( + visible: widget.showText, + child: SizedBox( + height: 32, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TDText( + context.resource.back, + style: TextStyle( + height: 1.2, + fontSize: 10, + color: color, + fontWeight: FontWeight.w600), + ), + TDText( + context.resource.top, + style: TextStyle( + height: 1.2, + fontSize: 10, + color: color, + fontWeight: FontWeight.w600), + ), + ], + )), + ) + ], + ), + )); + } +} diff --git a/tdesign-component/lib/src/components/badge/td_badge.dart b/tdesign-component/lib/src/components/badge/td_badge.dart new file mode 100644 index 000000000..8ef1cbee0 --- /dev/null +++ b/tdesign-component/lib/src/components/badge/td_badge.dart @@ -0,0 +1,308 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; + +enum TDBadgeType { + /// 红点样式 + redPoint, + + /// 消息样式 + message, + + /// 气泡样式 + bubble, + + /// 方形样式 + square, + + /// 角标样式 + subscript +} + +enum TDBadgeBorder { + /// 大圆角 8px + large, + + /// 小圆角 2px + small +} + +enum TDBadgeSize { + /// 宽 20px + large, + + /// 宽 16px + small, +} + +class TDBadge extends StatefulWidget { + const TDBadge(this.type, + {Key? key, + this.count, + this.maxCount = '99', + this.border = TDBadgeBorder.large, + this.size = TDBadgeSize.small, + this.color, + this.textColor, + this.message, + this.widthLarge = 32, + this.widthSmall = 12, + this.padding, + this.showZero = true}) + : super(key: key); + + /// 红点数量 + final String? count; + + /// 最大红点数量 + final String? maxCount; + + /// 红点样式 + final TDBadgeType type; + + /// 红点尺寸 + final TDBadgeSize size; + + /// 红点圆角大小 + final TDBadgeBorder border; + + /// 红点颜色 + final Color? color; + + /// 文字颜色 + final Color? textColor; + + /// 消息内容 + final String? message; + + /// 角标大三角形宽 + final double widthLarge; + + /// 角标小三角形宽 + final double widthSmall; + + /// 角标自定义padding + final EdgeInsetsGeometry? padding; + + /// 值为0是否显示 + final bool showZero; + + @override + State createState() => _TDBadgeState(); +} + +class _TDBadgeState extends State { + String badgeNum = ''; + + void updateBadgeNum(String? newCount) { + if (newCount == null) { + return; + } + setState(() { + // 如果 newCount 超过了 maxCount,则显示 `${maxCount}+` + final countValue = int.tryParse(newCount) ?? 0; + final maxCountValue = int.tryParse(widget.maxCount ?? '') ?? 0; + if (maxCountValue > 0 && countValue > maxCountValue) { + badgeNum = '${maxCountValue}+'; + } else { + badgeNum = newCount; + } + }); + } + + double getBadgeSize() { + switch (widget.size) { + case TDBadgeSize.large: + return 20; + case TDBadgeSize.small: + return 16; + } + } + + Font? getBadgeFont(BuildContext context) { + switch (widget.size) { + case TDBadgeSize.large: + return TDTheme.of(context).fontMarkSmall; + case TDBadgeSize.small: + return TDTheme.of(context).fontMarkExtraSmall; + } + } + + bool isVisible() { + var value = getValue(); + try { + return widget.showZero || double.parse(value) != 0; + } catch (e) { + return true; + } + } + + String getValue() { + return ((widget.message ?? '').isNotEmpty + ? widget.message + : (widget.count ?? '').isNotEmpty + ? widget.count + : context.resource.badgeZero) as String; + } + + @override + Widget build(BuildContext context) { + updateBadgeNum(widget.count); + switch (widget.type) { + case TDBadgeType.redPoint: + return Container( + alignment: Alignment.center, + height: getBadgeSize() / 2, + width: getBadgeSize() / 2, + decoration: BoxDecoration( + color: widget.color ?? TDTheme.of(context).errorColor6, + borderRadius: BorderRadius.circular(getBadgeSize() / 4)), + ); + case TDBadgeType.message: + return Visibility( + visible: isVisible(), + child: badgeNum.length == 1 + ? Container( + height: getBadgeSize(), + width: getBadgeSize(), + decoration: BoxDecoration( + color: widget.color ?? TDTheme.of(context).errorColor6, + borderRadius: BorderRadius.circular(getBadgeSize() / 2), + ), + child: Center( + child: TDText( + widget.message ?? '$badgeNum', + forceVerticalCenter: true, + font: getBadgeFont(context), + fontWeight: FontWeight.w500, + textColor: + widget.textColor ?? TDTheme.of(context).whiteColor1, + textAlign: TextAlign.center, + ), + )) + : Container( + height: getBadgeSize(), + padding: const EdgeInsets.only(left: 5, right: 5), + decoration: BoxDecoration( + color: widget.color ?? TDTheme.of(context).errorColor6, + borderRadius: BorderRadius.circular(getBadgeSize() / 2), + ), + child: Center( + child: TDText( + widget.message ?? '$badgeNum', + forceVerticalCenter: true, + font: getBadgeFont(context), + fontWeight: FontWeight.w500, + textColor: + widget.textColor ?? TDTheme.of(context).whiteColor1, + textAlign: TextAlign.center, + ), + ), + )); + case TDBadgeType.subscript: + return ClipPath( + clipper: TrapezoidPath(widget.widthLarge, widget.widthSmall), + child: Container( + alignment: Alignment.topRight, + color: widget.color ?? TDTheme.of(context).errorColor6, + height: 32, + width: 32, + child: Transform.rotate( + angle: pi / 4, + child: Padding( + padding: widget.padding ?? + const EdgeInsets.only(left: 4, bottom: 8), + child: TDText( + widget.message ?? '$badgeNum', + font: getBadgeFont(context), + fontWeight: FontWeight.w500, + textColor: + widget.textColor ?? TDTheme.of(context).whiteColor1, + textAlign: TextAlign.center, + ), + )), + ), + ); + case TDBadgeType.bubble: + return Visibility( + visible: isVisible(), + child: Container( + height: 16, + padding: const EdgeInsets.only(left: 4, right: 4), + decoration: BoxDecoration( + color: widget.color ?? TDTheme.of(context).errorColor6, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(10), + topRight: Radius.circular(10), + bottomRight: Radius.circular(10), + bottomLeft: Radius.circular(1)), + ), + child: Center( + child: TDText( + widget.message ?? '$badgeNum', + forceVerticalCenter: true, + font: getBadgeFont(context), + fontWeight: FontWeight.w500, + textColor: + widget.textColor ?? TDTheme.of(context).whiteColor1, + textAlign: TextAlign.center, + ), + ), + )); + case TDBadgeType.square: + return Visibility( + visible: isVisible(), + child: IntrinsicWidth( + child: Container( + height: getBadgeSize(), + padding: const EdgeInsets.only(left: 5, right: 5), + decoration: BoxDecoration( + color: widget.color ?? TDTheme.of(context).errorColor6, + borderRadius: widget.border == TDBadgeBorder.large + ? BorderRadius.circular(8) + : BorderRadius.circular(2), + ), + child: Center( + child: TDText( + widget.message ?? '$badgeNum', + forceVerticalCenter: true, + font: getBadgeFont(context), + fontWeight: FontWeight.w500, + textColor: + widget.textColor ?? TDTheme.of(context).whiteColor1, + textAlign: TextAlign.center, + ), + ), + ) + ) + ); + } + } +} + +class TrapezoidPath extends CustomClipper { + final double widthLarge; + final double widthSmall; + + TrapezoidPath(this.widthLarge, this.widthSmall); + + @override + Path getClip(Size size) { + var path = Path(); + path.moveTo(0, 0); + path.lineTo(widthLarge - widthSmall, 0); + path.lineTo(widthLarge, widthSmall); + path.lineTo(widthLarge, widthLarge); + path.lineTo(0, 0); + return path; + } + + @override + bool shouldReclip(CustomClipper oldClipper) { + return true; + } +} diff --git a/tdesign-component/lib/src/components/button/td_button.dart b/tdesign-component/lib/src/components/button/td_button.dart new file mode 100644 index 000000000..d9861f45e --- /dev/null +++ b/tdesign-component/lib/src/components/button/td_button.dart @@ -0,0 +1,470 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +enum TDButtonSize { large, medium, small, extraSmall } + +enum TDButtonType { fill, outline, text, ghost } + +enum TDButtonShape { rectangle, round, square, circle, filled } + +enum TDButtonTheme { defaultTheme, primary, danger, light } + +enum TDButtonStatus { defaultState, active, disable } + +enum TDButtonIconPosition { left, right } + +typedef TDButtonEvent = void Function(); + +/// TD常规按钮 +class TDButton extends StatefulWidget { + const TDButton( + {Key? key, + this.text, + this.size = TDButtonSize.medium, + this.type = TDButtonType.fill, + this.shape = TDButtonShape.rectangle, + this.theme, + this.child, + this.disabled = false, + this.isBlock = false, + this.style, + this.activeStyle, + this.disableStyle, + this.textStyle, + this.disableTextStyle, + this.width, + this.height, + this.onTap, + this.icon, + this.iconWidget, + this.iconTextSpacing, + this.onLongPress, + this.margin, + this.padding, + this.iconPosition = TDButtonIconPosition.left}) + : super(key: key); + + /// 自控件 + final Widget? child; + + /// 文本内容 + final String? text; + + /// 禁止点击 + final bool disabled; + + /// 自定义宽度 + final double? width; + + /// 自定义高度 + final double? height; + + /// 尺寸 + final TDButtonSize size; + + /// 类型:填充,描边,文字 + final TDButtonType type; + + /// 形状:圆角,胶囊,方形,圆形,填充 + final TDButtonShape shape; + + /// 主题 + final TDButtonTheme? theme; + + /// 自定义样式,有则优先用它,没有则根据type和theme选取.如果设置了style,则activeStyle和disableStyle也应该设置 + final TDButtonStyle? style; + + /// 自定义点击样式,有则优先用它,没有则根据type和theme选取 + final TDButtonStyle? activeStyle; + + /// 自定义禁用样式,有则优先用它,没有则根据type和theme选取 + final TDButtonStyle? disableStyle; + + /// 自定义可点击状态文本样式 + final TextStyle? textStyle; + + /// 自定义不可点击状态文本样式 + final TextStyle? disableTextStyle; + + /// 点击事件 + final TDButtonEvent? onTap; + + /// 长按事件 + final TDButtonEvent? onLongPress; + + /// 图标icon + final IconData? icon; + + /// 自定义图标icon控件 + final Widget? iconWidget; + + /// 自定义图标与文本之间距离 + final double? iconTextSpacing; + + /// 图标位置 + final TDButtonIconPosition? iconPosition; + + /// 自定义padding + final EdgeInsetsGeometry? padding; + + /// 自定义margin + final EdgeInsetsGeometry? margin; + + /// 是否为通栏按钮 + final bool isBlock; + + @override + State createState() => _TDButtonState(); +} + +class _TDButtonState extends State { + TDButtonStatus _buttonStatus = TDButtonStatus.defaultState; + TDButtonStyle? _innerDefaultStyle; + TDButtonStyle? _innerActiveStyle; + TDButtonStyle? _innerDisableStyle; + double? _width; + double? _height; + EdgeInsetsGeometry? _margin; + Alignment? _alignment; + TextStyle? _textStyle; + double? _iconSize; + + _updateParams() async { + _buttonStatus = + widget.disabled ? TDButtonStatus.disable : TDButtonStatus.defaultState; + _innerDefaultStyle = widget.style; + _innerActiveStyle = widget.activeStyle; + _innerDisableStyle = widget.disableStyle; + _width = _getWidth(); + _height = _getHeight(); + _margin = _getMargin(); + _alignment = widget.shape == TDButtonShape.filled || widget.isBlock + ? Alignment.center + : null; + if (widget.text != null) { + _textStyle = widget.disabled ? widget.disableTextStyle : widget.textStyle; + } + if (widget.icon != null) { + _iconSize = _getIconSize(); + } + } + + TDButtonStyle get style { + switch (_buttonStatus) { + case TDButtonStatus.defaultState: + return _defaultStyle; + case TDButtonStatus.active: + return _activeStyle; + case TDButtonStatus.disable: + return _disableStyle; + } + } + + @override + void initState() { + super.initState(); + _updateParams(); + } + + @override + Widget build(BuildContext context) { + Widget display = Container( + width: _width, + height: _height, + alignment: _alignment, + padding: _getPadding(), + margin: _margin, + decoration: BoxDecoration( + color: style.backgroundColor, + border: _getBorder(context), + borderRadius: style.radius ?? BorderRadius.all(_getRadius()), + ), + child: widget.child ?? _getChild(), + ); + + if (widget.disabled) { + return display; + } + return GestureDetector( + child: display, + onTap: widget.onTap, + onLongPress: widget.onLongPress, + onTapDown: (TapDownDetails details) { + if (widget.disabled) { + return; + } + setState(() { + _buttonStatus = TDButtonStatus.active; + }); + }, + onTapUp: (TapUpDetails details) { + Future.delayed(const Duration(milliseconds: 100), () { + if (mounted && !widget.disabled) { + setState(() { + _buttonStatus = TDButtonStatus.defaultState; + }); + } + }); + }, + onTapCancel: () { + if (widget.disabled) { + return; + } + setState(() { + _buttonStatus = TDButtonStatus.defaultState; + }); + }, + ); + } + + Border? _getBorder(BuildContext context) { + if (style.frameWidth != null && style.frameWidth != 0) { + return Border.all( + color: style.frameColor ?? TDTheme.of(context).grayColor3, + width: style.frameWidth!, + ); + } + return null; + } + + Widget _getChild() { + var icon = getIcon(); + if (widget.text == null && icon == null) { + return Container(); + } + var children = []; + // 系统Icon会导致不居中,因此自绘icon指定height + if (icon != null && widget.iconPosition == TDButtonIconPosition.left) { + children.add(icon); + } + if (widget.text != null) { + var text = TDText( + widget.text!, + font: _getTextFont(), + textColor: style.textColor ?? TDTheme.of(context).fontGyColor1, + style: _textStyle, + forceVerticalCenter: true, + ); + children.add(text); + } + if (icon != null && widget.iconPosition == TDButtonIconPosition.right) { + children.add(icon); + } + + if (children.length == 2) { + children.insert( + 1, + SizedBox( + width:widget.iconTextSpacing??8, + ), + ); + } + return Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: children, + ); + } + + Widget? getIcon() { + if (widget.iconWidget != null) { + return widget.iconWidget; + } + if (widget.icon != null) { + return RichText( + overflow: TextOverflow.visible, + text: TextSpan( + text: String.fromCharCode(widget.icon!.codePoint), + style: TextStyle( + inherit: false, + color: style.textColor, + height: 1, + fontSize: _iconSize, + fontFamily: widget.icon!.fontFamily, + package: widget.icon!.fontPackage, + ), + ), + ); + } + return null; + } + + Font _getTextFont() { + switch (widget.size) { + case TDButtonSize.large: + return TDTheme.of(context).fontMarkLarge ?? + Font(size: 16, lineHeight: 24); + case TDButtonSize.medium: + return TDTheme.of(context).fontMarkLarge ?? + Font(size: 16, lineHeight: 24); + case TDButtonSize.small: + return TDTheme.of(context).fontMarkMedium ?? + Font(size: 14, lineHeight: 22); + case TDButtonSize.extraSmall: + return TDTheme.of(context).fontMarkMedium ?? + Font(size: 14, lineHeight: 22); + } + } + + double? _getWidth() { + if (widget.width != null) { + return widget.width; + } + if (!widget.isBlock && + (widget.shape == TDButtonShape.square || + widget.shape == TDButtonShape.circle)) { + switch (widget.size) { + case TDButtonSize.large: + return 48; + case TDButtonSize.medium: + return 40; + case TDButtonSize.small: + return 32; + case TDButtonSize.extraSmall: + return 28; + } + } + return null; + } + + double _getHeight() { + if (widget.height != null) { + return widget.height!; + } + switch (widget.size) { + case TDButtonSize.large: + return 48; + case TDButtonSize.medium: + return 40; + case TDButtonSize.small: + return 32; + case TDButtonSize.extraSmall: + return 28; + } + } + + double _getIconSize() { + switch (widget.size) { + case TDButtonSize.large: + return 24; + case TDButtonSize.medium: + return 20; + case TDButtonSize.small: + return 18; + case TDButtonSize.extraSmall: + return 14; + } + } + + EdgeInsetsGeometry? _getMargin() { + if (widget.margin != null) { + return widget.margin; + } + return widget.isBlock ? const EdgeInsets.only(left: 16, right: 16) : null; + } + + EdgeInsetsGeometry? _getPadding() { + if (widget.padding != null) { + return widget.padding; + } + var equalSide = widget.shape == TDButtonShape.square || + widget.shape == TDButtonShape.circle; + + double horizontalPadding; + double verticalPadding; + switch (widget.size) { + case TDButtonSize.large: + horizontalPadding = equalSide ? 12 : 20; + verticalPadding = 12; + break; + case TDButtonSize.medium: + horizontalPadding = equalSide ? 10 : 16; + verticalPadding = equalSide ? 10 : 8; + break; + case TDButtonSize.small: + horizontalPadding = equalSide ? 7 : 12; + verticalPadding = equalSide ? 7 : 5; + break; + case TDButtonSize.extraSmall: + horizontalPadding = equalSide ? 5 : 8; + verticalPadding = equalSide ? 5 : 3; + break; + } + if (style.frameWidth != null && style.frameWidth != 0) { + horizontalPadding = horizontalPadding - style.frameWidth!; + verticalPadding = verticalPadding - style.frameWidth!; + if (horizontalPadding < 0) { + horizontalPadding = 0; + } + if (verticalPadding < 0) { + verticalPadding = 0; + } + } + return EdgeInsets.only( + left: horizontalPadding, + right: horizontalPadding, + bottom: verticalPadding, + top: verticalPadding); + } + + @override + void didUpdateWidget(covariant TDButton oldWidget) { + super.didUpdateWidget(oldWidget); + _updateParams(); + } + + TDButtonStyle _generateInnerStyle() { + switch (widget.type) { + case TDButtonType.fill: + return TDButtonStyle.generateFillStyleByTheme( + context, widget.theme, _buttonStatus); + case TDButtonType.outline: + return TDButtonStyle.generateOutlineStyleByTheme( + context, widget.theme, _buttonStatus); + case TDButtonType.text: + return TDButtonStyle.generateTextStyleByTheme( + context, widget.theme, _buttonStatus); + case TDButtonType.ghost: + return TDButtonStyle.generateGhostStyleByTheme( + context, widget.theme, _buttonStatus); + } + } + + Radius _getRadius() { + switch (widget.shape) { + case TDButtonShape.rectangle: + case TDButtonShape.square: + return Radius.circular(TDTheme.of(context).radiusDefault); + case TDButtonShape.round: + case TDButtonShape.circle: + return Radius.circular(TDTheme.of(context).radiusRound); + case TDButtonShape.filled: + return Radius.zero; + } + } + + TDButtonStyle get _defaultStyle { + if (_innerDefaultStyle != null) { + return _innerDefaultStyle!; + } + _innerDefaultStyle = widget.style ?? _generateInnerStyle(); + return _innerDefaultStyle!; + } + + TDButtonStyle get _activeStyle { + if (_innerActiveStyle != null) { + return _innerActiveStyle!; + } + _innerActiveStyle = widget.style ?? _generateInnerStyle(); + return _innerActiveStyle!; + } + + TDButtonStyle get _disableStyle { + if (_innerDisableStyle != null) { + return _innerDisableStyle!; + } + _innerDisableStyle = widget.style ?? _generateInnerStyle(); + return _innerDisableStyle!; + } +} diff --git a/tdesign-component/lib/src/components/button/td_button_style.dart b/tdesign-component/lib/src/components/button/td_button_style.dart new file mode 100644 index 000000000..131674b13 --- /dev/null +++ b/tdesign-component/lib/src/components/button/td_button_style.dart @@ -0,0 +1,195 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +/// TDButton按钮样式 +class TDButtonStyle { + /// 背景颜色 + Color? backgroundColor; + + /// 边框颜色 + Color? frameColor; + + /// 文字颜色 + Color? textColor; + + /// 边框宽度 + double? frameWidth; + + /// 自定义圆角 + BorderRadiusGeometry? radius; + + TDButtonStyle({this.backgroundColor, this.frameColor, this.textColor, this.frameWidth, this.radius}); + + /// 生成不同主题的填充按钮样式 + TDButtonStyle.generateFillStyleByTheme(BuildContext context, TDButtonTheme? theme, TDButtonStatus status) { + switch (theme) { + case TDButtonTheme.primary: + textColor = TDTheme.of(context).fontWhColor1; + backgroundColor = _getBrandColor(context, status); + break; + case TDButtonTheme.danger: + textColor = TDTheme.of(context).fontWhColor1; + backgroundColor = _getErrorColor(context, status); + break; + case TDButtonTheme.light: + textColor = _getBrandColor(context, status); + backgroundColor = _getLightColor(context, status); + break; + case TDButtonTheme.defaultTheme: + default: + textColor = _getDefaultTextColor(context, status); + backgroundColor = _getDefaultBgColor(context, status); + } + frameColor = backgroundColor; + } + + /// 生成不同主题的描边按钮样式 + TDButtonStyle.generateOutlineStyleByTheme(BuildContext context, TDButtonTheme? theme, TDButtonStatus status) { + switch (theme) { + case TDButtonTheme.primary: + textColor = _getBrandColor(context, status); + backgroundColor = + status == TDButtonStatus.active ? TDTheme.of(context).grayColor3 : TDTheme.of(context).whiteColor1; + frameColor = textColor; + break; + case TDButtonTheme.danger: + textColor = _getErrorColor(context, status); + backgroundColor = + status == TDButtonStatus.active ? TDTheme.of(context).grayColor3 : TDTheme.of(context).whiteColor1; + frameColor = textColor; + break; + case TDButtonTheme.light: + textColor = _getBrandColor(context, status); + backgroundColor = _getLightColor(context, status); + frameColor = textColor; + break; + case TDButtonTheme.defaultTheme: + default: + textColor = _getDefaultTextColor(context, status); + backgroundColor = _getOutlineDefaultBgColor(context, status); + frameColor = TDTheme.of(context).grayColor4; + } + frameWidth = 1; + } + + /// 生成不同主题的文本按钮样式 + TDButtonStyle.generateTextStyleByTheme(BuildContext context, TDButtonTheme? theme, TDButtonStatus status) { + switch (theme) { + case TDButtonTheme.primary: + textColor = _getBrandColor(context, status); + backgroundColor = status == TDButtonStatus.active ? TDTheme.of(context).grayColor3 : Colors.transparent; + break; + case TDButtonTheme.danger: + textColor = _getErrorColor(context, status); + backgroundColor = status == TDButtonStatus.active ? TDTheme.of(context).grayColor3 : Colors.transparent; + break; + case TDButtonTheme.light: + textColor = _getBrandColor(context, status); + backgroundColor = status == TDButtonStatus.active ? TDTheme.of(context).grayColor3 : Colors.transparent; + break; + case TDButtonTheme.defaultTheme: + default: + textColor = _getDefaultTextColor(context, status); + backgroundColor = status == TDButtonStatus.active ? TDTheme.of(context).grayColor3 : Colors.transparent; + } + frameColor = backgroundColor; + } + + /// 生成不同主题的幽灵按钮样式 + TDButtonStyle.generateGhostStyleByTheme(BuildContext context, TDButtonTheme? theme, TDButtonStatus status) { + switch (theme) { + case TDButtonTheme.primary: + textColor = + status == TDButtonStatus.disable ? TDTheme.of(context).fontWhColor4 : _getBrandColor(context, status); + break; + case TDButtonTheme.danger: + textColor = + status == TDButtonStatus.disable ? TDTheme.of(context).fontWhColor4 : _getErrorColor(context, status); + break; + case TDButtonTheme.light: + textColor = + status == TDButtonStatus.disable ? TDTheme.of(context).fontWhColor4 : _getBrandColor(context, status); + break; + case TDButtonTheme.defaultTheme: + default: + switch (status) { + case TDButtonStatus.active: + textColor = TDTheme.of(context).fontWhColor2; + break; + case TDButtonStatus.disable: + textColor = TDTheme.of(context).fontWhColor4; + break; + default: + textColor = TDTheme.of(context).fontWhColor1; + } + } + backgroundColor = Colors.transparent; + frameColor = textColor; + frameWidth = 1; + } + + Color _getBrandColor(BuildContext context, TDButtonStatus status) { + switch (status) { + case TDButtonStatus.defaultState: + return TDTheme.of(context).brandNormalColor; + case TDButtonStatus.active: + return TDTheme.of(context).brandClickColor; + case TDButtonStatus.disable: + return TDTheme.of(context).brandDisabledColor; + } + } + + Color _getLightColor(BuildContext context, TDButtonStatus status) { + switch (status) { + case TDButtonStatus.defaultState: + case TDButtonStatus.disable: + return TDTheme.of(context).brandLightColor; + case TDButtonStatus.active: + return TDTheme.of(context).brandFocusColor; + } + } + + Color _getErrorColor(BuildContext context, TDButtonStatus status) { + switch (status) { + case TDButtonStatus.defaultState: + return TDTheme.of(context).errorNormalColor; + case TDButtonStatus.active: + return TDTheme.of(context).errorClickColor; + case TDButtonStatus.disable: + return TDTheme.of(context).errorDisabledColor; + } + } + + Color _getDefaultBgColor(BuildContext context, TDButtonStatus status) { + switch (status) { + case TDButtonStatus.defaultState: + return TDTheme.of(context).grayColor3; + case TDButtonStatus.active: + return TDTheme.of(context).grayColor5; + case TDButtonStatus.disable: + return TDTheme.of(context).grayColor2; + } + } + + Color _getDefaultTextColor(BuildContext context, TDButtonStatus status) { + switch (status) { + case TDButtonStatus.defaultState: + case TDButtonStatus.active: + return TDTheme.of(context).fontGyColor1; + case TDButtonStatus.disable: + return TDTheme.of(context).fontGyColor4; + } + } + + Color _getOutlineDefaultBgColor(BuildContext context, TDButtonStatus status) { + switch (status) { + case TDButtonStatus.defaultState: + return TDTheme.of(context).whiteColor1; + case TDButtonStatus.active: + return TDTheme.of(context).grayColor3; + case TDButtonStatus.disable: + return TDTheme.of(context).grayColor2; + } + } +} diff --git a/tdesign-component/lib/src/components/calendar/td_calendar.dart b/tdesign-component/lib/src/components/calendar/td_calendar.dart new file mode 100644 index 000000000..9c7a8e2ed --- /dev/null +++ b/tdesign-component/lib/src/components/calendar/td_calendar.dart @@ -0,0 +1,362 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; +import '../../util/iterable_ext.dart'; + +export 'td_calendar_body.dart'; +export 'td_calendar_cell.dart'; +export 'td_calendar_header.dart'; +export 'td_calendar_popup.dart'; +export 'td_calendar_style.dart'; + +typedef CalendarFormat = TDate? Function(TDate? day); + +enum CalendarType { single, multiple, range } + +enum CalendarTrigger { closeBtn, confirmBtn, overlay } + +enum DateSelectType { selected, disabled, start, centre, end, empty } + +/// 日历组件 +class TDCalendar extends StatefulWidget { + const TDCalendar({ + Key? key, + this.firstDayOfWeek = 0, + this.format, + this.maxDate, + this.minDate, + this.title, + this.titleWidget, + this.type = CalendarType.single, + this.value, + this.displayFormat = 'year month', + this.cellHeight = 60, + this.height, + this.width, + this.style, + this.onChange, + this.onCellClick, + this.onCellLongPress, + this.onHeaderClick, + this.useTimePicker = false, + this.timePickerModel, + this.monthTitleHeight = 22, + this.monthTitleBuilder, + this.pickerHeight = 178, + this.pickerItemCount = 3, + this.isTimeUnit = true, + this.animateTo = false, + this.cellWidget, + }) : super(key: key); + + /// 第一天从星期几开始,默认 0 = 周日 + final int? firstDayOfWeek; + + /// 用于格式化日期的函数,可定义日期前后的显示内容和日期样式 + final CalendarFormat? format; + + /// 最大可选的日期(fromMillisecondsSinceEpoch),不传则默认半年后 + final int? maxDate; + + /// 最小可选的日期(fromMillisecondsSinceEpoch),不传则默认今天 + final int? minDate; + + /// 标题 + final String? title; + + /// 标题组件 + final Widget? titleWidget; + + /// 日历的选择类型,single = 单选;multiple = 多选; range = 区间选择 + final CalendarType? type; + + /// 当前选择的日期(fromMillisecondsSinceEpoch),不传则默认今天,当 type = single 时数组长度为1 + final List? value; + + /// 年月显示格式,`year`表示年,`month`表示月,如`year month`表示年在前、月在后、中间隔一个空格 + final String? displayFormat; + + /// 高度 + final double? height; + + /// 日期高度 + final double? cellHeight; + + /// 宽度 + final double? width; + + /// 自定义样式 + final TDCalendarStyle? style; + + /// 选中值变化时触发 + final void Function(List value)? onChange; + + /// 点击日期时触发 + final void Function(int value, DateSelectType type, TDate tdate)? onCellClick; + + /// 长安日期时触发 + final void Function(int value, DateSelectType type, TDate tdate)? onCellLongPress; + + /// 点击周时触发 + final void Function(int index, String week)? onHeaderClick; + + /// 是否显示时间选择器 + final bool? useTimePicker; + + /// 自定义时间选择器 + final List? timePickerModel; + + /// 月标题高度 + final double? monthTitleHeight; + + /// 月标题构建器 + final Widget Function(BuildContext context, DateTime monthDate)? monthTitleBuilder; + + /// 时间选择器List的视窗高度 + final double? pickerHeight; + + /// 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 + final int? pickerItemCount; + + /// 是否显示时间单位 + final bool? isTimeUnit; + + /// 动画滚动到指定位置 + final bool? animateTo; + + /// 自定义日期单元格组件 + final Widget? Function(BuildContext context, TDate tdate, DateSelectType selectType)? cellWidget; + + List? get _value => value?.map((e) { + final date = DateTime.fromMillisecondsSinceEpoch(e); + return DateTime(date.year, date.month, date.day); + }).toList(); + + List? get _valueTime => value?.map((item) { + return DateTime.fromMillisecondsSinceEpoch(item); + }).toList(); + + @override + _TDCalendarState createState() => _TDCalendarState(); +} + +class _TDCalendarState extends State { + late List weekdayNames; + late List monthNames; + late TDCalendarInherited? inherited; + late TDCalendarStyle _style; + final List timePickerModelList = []; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + weekdayNames = [ + context.resource.sunday, + context.resource.monday, + context.resource.tuesday, + context.resource.wednesday, + context.resource.thursday, + context.resource.friday, + context.resource.saturday, + ]; + monthNames = [ + context.resource.january, + context.resource.february, + context.resource.march, + context.resource.april, + context.resource.may, + context.resource.june, + context.resource.july, + context.resource.august, + context.resource.september, + context.resource.october, + context.resource.november, + context.resource.december, + ]; + _style = widget.style ?? TDCalendarStyle.generateStyle(context); + } + + @override + Widget build(BuildContext context) { + inherited = TDCalendarInherited.of(context); + _initValue(); + timePickerModelList.clear(); + final verticalGap = _style.verticalGap ?? TDTheme.of(context).spacer8; + return Container( + height: widget.height, + width: widget.width ?? double.infinity, + decoration: _style.decoration, + child: Column( + children: [ + TDCalendarHeader( + firstDayOfWeek: widget.firstDayOfWeek ?? 0, + weekdayGap: TDTheme.of(context).spacer4, + padding: TDTheme.of(context).spacer16, + weekdayStyle: _style.weekdayStyle, + weekdayHeight: 46, + title: widget.title, + titleStyle: _style.titleStyle, + titleWidget: widget.titleWidget, + titleMaxLine: _style.titleMaxLine, + titleOverflow: TextOverflow.ellipsis, + closeBtn: inherited?.usePopup ?? false, + closeColor: _style.titleCloseColor, + weekdayNames: weekdayNames, + onClose: inherited?.onClose, + onClick: widget.onHeaderClick, + ), + Expanded( + child: TDCalendarBody( + type: widget.type ?? CalendarType.single, + firstDayOfWeek: widget.firstDayOfWeek ?? 0, + maxDate: widget.maxDate, + minDate: widget.minDate, + value: widget._value, + bodyPadding: _style.bodyPadding ?? TDTheme.of(context).spacer16, + displayFormat: widget.displayFormat ?? 'year month', + monthNames: monthNames, + monthTitleStyle: _style.monthTitleStyle, + verticalGap: verticalGap, + cellHeight: widget.cellHeight ?? 60, + monthTitleHeight: widget.monthTitleHeight ?? 22, + monthTitleBuilder: widget.monthTitleBuilder, + animateTo: widget.animateTo ?? false, + builder: (date, dateList, data, rowIndex, colIndex) { + return TDCalendarCell( + height: widget.cellHeight ?? 60, + tdate: date, + format: widget.format, + type: widget.type ?? CalendarType.single, + data: data, + padding: verticalGap / 2, + onChange: (value) { + final time = _getValue(value); + inherited?.selected.value = time; + widget.onChange?.call(time); + }, + onCellClick: widget.onCellClick, + onCellLongPress: widget.onCellLongPress, + dateList: dateList, + rowIndex: rowIndex, + colIndex: colIndex, + cellWidget: widget.cellWidget, + ); + }, + ), + ), + if (widget.useTimePicker == true) _getTimePicker(), + if (inherited?.usePopup == true) + inherited?.confirmBtn ?? + Padding( + padding: EdgeInsets.symmetric(vertical: TDTheme.of(context).spacer16), + child: TDButton( + theme: TDButtonTheme.primary, + text: context.resource.confirm, + isBlock: true, + size: TDButtonSize.large, + onTap: inherited?.onConfirm, + ), + ), + ], + ), + ); + } + + Widget _getTimePicker() { + final noRange = widget.type != CalendarType.range; + final now = DateTime.now(); + final valueTime = widget._valueTime; + return Container( + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + boxShadow: const [ + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.04), + blurRadius: 12, + offset: Offset(0, -2), + ), + ], + ), + child: Row( + children: List.generate( + noRange ? 1 : 2, + (index) { + final timePickerModel = widget.timePickerModel?.getOrNull(index) ?? + DatePickerModel( + useYear: false, + useMonth: false, + useDay: false, + useWeekDay: false, + useHour: true, + useMinute: true, + useSecond: false, + dateStart: [1999, 01, 01], + dateEnd: [2999, 12, 31], + dateInitial: [ + ...[1999, 01, 01], + valueTime?.getOrNull(index)?.hour ?? now.hour, + valueTime?.getOrNull(index)?.minute ?? now.minute, + valueTime?.getOrNull(index)?.second ?? now.second + ], + ); + final timePicker = TDDatePicker( + title: noRange + ? context.resource.time + : index == 0 + ? context.resource.start + : context.resource.end, + leftText: '', + rightText: '', + model: timePickerModel, + pickerHeight: widget.pickerHeight ?? 178, + pickerItemCount: widget.pickerItemCount ?? 3, + isTimeUnit: widget.isTimeUnit ?? true, + onConfirm: (selected) {}, + onSelectedItemChanged: (wheelIndex, index) { + final time = _getValue(inherited?.selected.value ?? []); + inherited?.selected.value = time; + widget.onChange?.call(time); + }, + ); + timePickerModelList.add(timePickerModel); + return Expanded(child: timePicker); + }, + ), + ), + ); + } + + List _getValue(List value) { + var dateValue = value.map((e) { + final date = DateTime.fromMillisecondsSinceEpoch(e); + return DateTime(date.year, date.month, date.day).millisecondsSinceEpoch; + }).toList(); + if (widget.useTimePicker != true) { + return dateValue; + } + final milliseconds = timePickerModelList.map((model) { + final hour = model.useHour ? model.hourFixedExtentScrollController.selectedItem : 0; + final minute = model.useMinute ? model.minuteFixedExtentScrollController.selectedItem : 0; + final second = model.useSecond ? model.secondFixedExtentScrollController.selectedItem : 0; + return (hour * 60 * 60 + minute * 60 + second) * 1000; + }).toList(); + if (widget.type == CalendarType.range && dateValue.length == 1) { + dateValue.add(dateValue.first); + } + return dateValue.mapWidthIndex((e, index) { + if (widget.type != CalendarType.range) { + return e + (milliseconds.getOrNull(0) ?? 0); + } + return e + (milliseconds.getOrNull(index) ?? 0); + }).toList(); + } + + void _initValue() { + if (inherited == null) { + return; + } + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + inherited!.selected.value = _getValue(widget.value ?? []); + }); + } +} diff --git a/tdesign-component/lib/src/components/calendar/td_calendar_body.dart b/tdesign-component/lib/src/components/calendar/td_calendar_body.dart new file mode 100644 index 000000000..c84eca04c --- /dev/null +++ b/tdesign-component/lib/src/components/calendar/td_calendar_body.dart @@ -0,0 +1,240 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; +import '../../util/iterable_ext.dart'; + +class TDCalendarBody extends StatelessWidget { + const TDCalendarBody({ + Key? key, + this.maxDate, + this.minDate, + required this.type, + this.value, + required this.firstDayOfWeek, + required this.builder, + required this.bodyPadding, + required this.displayFormat, + required this.monthNames, + this.monthTitleStyle, + this.monthTitleBuilder, + required this.cellHeight, + required this.monthTitleHeight, + required this.verticalGap, + required this.animateTo, + }) : super(key: key); + + final int? maxDate; + final int? minDate; + final CalendarType type; + final List? value; + final int firstDayOfWeek; + final Widget Function( + TDate? date, List dateList, Map> data, int rowIndex, int colIndex) builder; + final double bodyPadding; + final String displayFormat; + final List monthNames; + final TextStyle? monthTitleStyle; + final Widget Function(BuildContext context, DateTime monthDate)? monthTitleBuilder; + final double monthTitleHeight; + final double verticalGap; + final double cellHeight; + final bool animateTo; + + @override + Widget build(BuildContext context) { + final scrollController = ScrollController(); + final min = _getDefDate(minDate); + final max = _getDefDate(maxDate, 6); + final months = _monthsBetween(min, max); + final data = >{}; + final monthHeight = {}; + _scrollToItem(scrollController, months, monthHeight); + return ListView.builder( + padding: EdgeInsets.all(bodyPadding), + controller: scrollController, + itemCount: months.length, + itemExtentBuilder: (index, dimensions) => _getMonthHeight(months, index, monthHeight), + itemBuilder: (context, index) { + final monthDate = months[index]; + final monthYear = monthDate.year.toString() + context.resource.year; + final monthMonth = monthNames[monthDate.month - 1]; + final monthDateText = displayFormat.replaceFirst('year', monthYear).replaceFirst('month', monthMonth); + late List monthData; + if (data.containsKey(monthDate)) { + monthData = data[monthDate]!; + } else { + monthData = data[monthDate] = _getDaysInMonth(monthDate, min, max); + } + + final keyList = [...data.keys]; + final currentIndex = keyList.indexOf(monthDate); + keyList.forEachWidthIndex((key, index) { + if (index < currentIndex - 10 || index > currentIndex + 10) { + // 保留最近 10 个月的数据,防止内存泄露 + data.remove(key); + } + }); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + height: monthTitleHeight, + child: monthTitleBuilder?.call(context, monthDate) ?? TDText(monthDateText, style: monthTitleStyle), + ), + ...List.generate( + (monthData.length / 7).ceil(), + (rowIndex) => [ + SizedBox(height: verticalGap), + Row( + children: List.generate( + 7, + (colIndex) => [ + if (colIndex != 0) SizedBox(width: verticalGap / 2), + Expanded( + child: builder( + monthData[rowIndex * 7 + colIndex], + monthData, + data, + rowIndex, + colIndex, + ), + ), + ], + ).expand((element) => element).toList(), + ), + ], + ).expand((element) => element).toList(), + SizedBox(height: index == months.length - 1 ? 0 : bodyPadding), + ], + ); + }, + ); + } + + void _scrollToItem(ScrollController scrollController, List months, Map monthHeight) { + if (value == null || value!.isEmpty) { + return; + } + final scrollDate = value!.reduce((a, b) => a.isBefore(b) ? a : b); + var lastMonthDay = DateTime(months.last.year, months.last.month + 1); + lastMonthDay = lastMonthDay.add(const Duration(days: -1)); + if (months.first.isAfter(scrollDate) || lastMonthDay.isBefore(scrollDate)) { + return; + } + WidgetsBinding.instance.addPostFrameCallback((_) { + var height = 0.0; + for (var i = 0; i < months.length; i++) { + final item = months[i]; + if (item.year == scrollDate.year && item.month == scrollDate.month) { + break; + } + height += (_getMonthHeight(months, i, monthHeight) ?? 0); + } + if (height <= 0) { + return; + } + if (animateTo) { + scrollController.animateTo( + height, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + ); + } else { + scrollController.jumpTo(height); + } + }); + } + + DateTime _getDefDate(int? date, [int? addMonth]) { + final now = date == null ? DateTime.now() : DateTime.fromMillisecondsSinceEpoch(date); + if (addMonth == null) { + return DateTime(now.year, now.month, now.day); + } + final month = now.month + addMonth; + return DateTime(now.year, date == null ? month : now.month, now.day); + } + + List _monthsBetween(DateTime min, DateTime max) { + final months = []; + var current = DateTime(min.year, min.month); + while (current.compareTo(max) <= 0) { + months.add(current); + current = DateTime(current.year, current.month + 1); + } + return months; + } + + List _getDaysInMonth(DateTime curDate, DateTime min, DateTime max) { + final daysInMonth = List.generate(_getPreOffset(curDate), (index) => null); + final daysInMonthCount = DateTime(curDate.year, curDate.month + 1, 0).day; // 获取下个月的第一天的前一天,即当前月的最后一天 + for (var day = 1; day <= daysInMonthCount; day++) { + final date = DateTime(curDate.year, curDate.month, day); + var selectType = DateSelectType.empty; + if (date.compareTo(min) == -1 || date.compareTo(max) == 1) { + selectType = DateSelectType.disabled; + } else if (type == CalendarType.single && (value?.length ?? 0) >= 1) { + if (date.compareTo(value![0]) == 0) { + selectType = DateSelectType.selected; + } + } else if (type == CalendarType.multiple && value != null) { + if (value!.isContains((e) => date.compareTo(e) == 0)) { + selectType = DateSelectType.selected; + } + } else if (type == CalendarType.range && (value?.length ?? 0) >= 1) { + final end = (value?.length ?? 0) > 1 ? value![1] : null; + if (date.compareTo(value![0]) == 0) { + selectType = DateSelectType.start; + } + if (end != null && value![0].compareTo(end) < 0) { + if (date.compareTo(end) == 0) { + selectType = DateSelectType.end; + } + if (date.compareTo(value![0]) == 1 && date.compareTo(end) == -1) { + selectType = DateSelectType.centre; + } + } + } + daysInMonth.add(TDate( + date: date, + typeNotifier: DateSelectTypeNotifier(selectType), + isLastDayOfMonth: daysInMonthCount == day, + )); + } + var sufOffset = 7 - daysInMonth.length % 7; + sufOffset = sufOffset == 7 ? 0 : sufOffset; + List.generate(sufOffset, (index) => daysInMonth.add(null)); + return daysInMonth; + } + + int _getPreOffset(DateTime date) { + final year = date.year; + final month = date.month; + var dayOneWeek = DateTime(year, month).weekday; + dayOneWeek = dayOneWeek == 7 ? 0 : dayOneWeek; + var preOffset = dayOneWeek - firstDayOfWeek; + preOffset = preOffset < 0 ? preOffset + 7 : preOffset; + return preOffset; + } + + /// 获取月份高度,带缓存 + double _getMonthHeight(List months, int index, Map monthHeight) { + if (months.getOrNull(index) == null) { + return 1; + } + if (monthHeight.containsKey(index)) { + return monthHeight[index]!; + } + final item = months[index]; + final isLast = index == months.length - 1; + final preOffset = _getPreOffset(item); + final daysInMonthCount = DateTime(item.year, item.month + 1, 0).day; + final daysInMonth = preOffset + daysInMonthCount; + final height = + monthTitleHeight + (daysInMonth / 7).ceil() * (verticalGap + cellHeight) + (isLast ? 0 : bodyPadding); + monthHeight[index] = height; + return height; + } +} diff --git a/tdesign-component/lib/src/components/calendar/td_calendar_cell.dart b/tdesign-component/lib/src/components/calendar/td_calendar_cell.dart new file mode 100644 index 000000000..c05ae9f96 --- /dev/null +++ b/tdesign-component/lib/src/components/calendar/td_calendar_cell.dart @@ -0,0 +1,308 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; +import '../../util/iterable_ext.dart'; +import '../../util/list_ext.dart'; + +class TDCalendarCell extends StatefulWidget { + const TDCalendarCell({ + Key? key, + this.tdate, + this.format, + required this.type, + this.onCellClick, + this.onCellLongPress, + this.onChange, + required this.height, + required this.data, + required this.padding, + required this.rowIndex, + required this.colIndex, + required this.dateList, + this.cellWidget, + }) : super(key: key); + + final TDate? tdate; + final CalendarFormat? format; + final CalendarType type; + final void Function(int value, DateSelectType type, TDate tdate)? onCellClick; + final void Function(int value, DateSelectType type, TDate tdate)? onCellLongPress; + final void Function(List value)? onChange; + final double height; + final Map> data; + final double padding; + final int rowIndex; + final int colIndex; + final List dateList; + final Widget? Function(BuildContext context, TDate tdate, DateSelectType selectType)? cellWidget; + + @override + _TDCalendarCellState createState() => _TDCalendarCellState(); +} + +class _TDCalendarCellState extends State { + var isToday = false; + var positionOffset = 0; + @override + void initState() { + super.initState(); + isToday = _isToday(); + widget.tdate?.typeNotifier.addListener(_cellTypeChange); + } + + @override + void didUpdateWidget(TDCalendarCell oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.tdate != oldWidget.tdate) { + isToday = _isToday(); + oldWidget.tdate?.typeNotifier.removeListener(_cellTypeChange); + widget.tdate?.typeNotifier.addListener(_cellTypeChange); + } + } + + @override + void dispose() { + widget.tdate?.typeNotifier.removeListener(_cellTypeChange); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (widget.tdate == null) { + return const SizedBox.shrink(); + } + final tdate = widget.format?.call(widget.tdate) ?? widget.tdate!; + final cellStyle = TDCalendarStyle.cellStyle(context, widget.tdate!._type); + final decoration = tdate.decoration ?? cellStyle.cellDecoration; + final positionColor = _getColor(cellStyle, decoration); + + // 新增自定义cell内容判断逻辑 + final content = widget.cellWidget?.call(context, tdate, widget.tdate!._type) ?? + Column( + children: [ + Expanded( + flex: 2, + child: tdate.prefixWidget ?? + TDText( + tdate.prefix ?? '', + style: tdate.prefixStyle ?? cellStyle.cellPrefixStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + Expanded( + flex: 3, + child: Center( + child: TDText( + forceVerticalCenter: true, + widget.tdate!.date.day.toString(), + style: (isToday ? cellStyle.todayStyle : null) ?? tdate.style ?? cellStyle.cellStyle, + ), + ), + ), + Expanded( + flex: 2, + child: tdate.suffixWidget ?? + TDText( + tdate.suffix ?? '', + style: tdate.suffixStyle ?? cellStyle.cellSuffixStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ); + + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: _cellTap, + onLongPress: () { + final selectType = widget.tdate!._type; + final curDate = widget.tdate!._milliseconds; + widget.onCellLongPress?.call(curDate, selectType, widget.tdate!); + }, + child: Stack( + clipBehavior: Clip.none, + children: [ + Container( + height: widget.height, + decoration: decoration, + padding: EdgeInsets.all(widget.padding), + child: content, // 使用自定义内容 + ), + if (widget.colIndex < 6) + Positioned( + right: -widget.padding - positionOffset, + child: Container( + width: widget.padding + 2 * positionOffset, + height: widget.height, + color: positionColor, + ), + ), + ], + ), + ); + } + + void _cellTap() { + final list = widget.data.values.expand((element) => element).toList(); + final selectType = widget.tdate!._type; + final curDate = widget.tdate!._milliseconds; + if (selectType == DateSelectType.disabled) { + widget.onCellClick?.call(curDate, selectType, widget.tdate!); + return; + } + switch (widget.type) { + case CalendarType.single: + final date = list.find((item) => item?._type == DateSelectType.selected); + date?._setType(DateSelectType.empty); + widget.tdate!._setType(DateSelectType.selected); + if (date?._milliseconds != curDate) { + widget.onChange?.call([curDate]); + } + break; + case CalendarType.multiple: + final date = list.where((item) => item?._type == DateSelectType.selected).toList(); + final value = date.map((item) => item!._milliseconds).toList(); + if (date.find((item) => item?._milliseconds == curDate) != null) { + widget.tdate!._setType(DateSelectType.empty); + value.remove(curDate); + } else { + widget.tdate!._setType(DateSelectType.selected); + value.add(curDate); + } + widget.onChange?.call(value); + break; + case CalendarType.range: + final start = list.find((item) => item?._type == DateSelectType.start); + final end = list.find((item) => item?._type == DateSelectType.end); + final startTimes = start?._milliseconds; + if ((start == null && end == null) || + (start != null && end != null) || + (start != null && end == null && startTimes! >= curDate)) { + start?._setType(DateSelectType.empty); + end?._setType(DateSelectType.empty); + final centres = list.where((item) => item?._type == DateSelectType.centre).toList(); + centres.forEach((item) => item!._setType(DateSelectType.empty)); + widget.tdate!._setType(DateSelectType.start); + widget.onChange?.call([curDate]); + } else if (start != null && end == null && startTimes! < curDate) { + start._setType(DateSelectType.start); + widget.tdate!._setType(DateSelectType.end); + var startIndex = list.indexOf(start) + 1; + while (list[startIndex] == null || list[startIndex]!._milliseconds < curDate) { + list[startIndex]?._setType(DateSelectType.centre); + startIndex++; + } + widget.onChange?.call([startTimes, curDate]); + } + break; + } + widget.onCellClick?.call(curDate, widget.tdate!._type, widget.tdate!); + } + + void _cellTypeChange() { + setState(() {}); + } + + Color? _getColor(TDCalendarStyle cellStyle, BoxDecoration? decoration) { + positionOffset = 0; + final next = _nextDay(); + if (widget.tdate?._type == DateSelectType.start) { + if (widget.tdate?.isLastDayOfMonth == true) { + return null; + } + if (next?._type == DateSelectType.end) { + positionOffset = 1; + return decoration?.color; + } + if (next?._type == DateSelectType.centre) { + return cellStyle.centreColor; + } + } + if (widget.tdate?._type == DateSelectType.centre) { + return cellStyle.centreColor; + } + return null; + } + + TDate? _nextDay([int num = 1]) { + final index = widget.rowIndex * 7 + widget.colIndex + num; + final date = widget.dateList.getOrNull(index); + return date; + } + + bool _isToday() { + final today = DateTime.now(); + return widget.tdate?._milliseconds == DateTime(today.year, today.month, today.day).millisecondsSinceEpoch; + } +} + +/// 时间对象 +class TDate { + TDate({ + required this.date, + required this.typeNotifier, + this.prefix, + this.prefixStyle, + this.prefixWidget, + this.suffix, + this.suffixStyle, + this.suffixWidget, + this.style, + this.decoration, + required this.isLastDayOfMonth, + }); + + /// 时间对象 + final DateTime date; + + /// 日期类型 + final DateSelectTypeNotifier typeNotifier; + + /// 日期前面的字符串 + String? prefix; + + /// 日期前面的字符串的样式 + TextStyle? prefixStyle; + + /// 日期前面的组件,优先级高于[prefix] + Widget? prefixWidget; + + /// 日期后面的字符串 + String? suffix; + + /// 日期后面的字符串的样式 + TextStyle? suffixStyle; + + /// 日期后面的组件,优先级高于[suffix] + Widget? suffixWidget; + + /// 日期样式 + TextStyle? style; + + /// 日期Decoration + BoxDecoration? decoration; + + /// 是否是当月最后一天 + final bool isLastDayOfMonth; + + int get _milliseconds => DateTime(date.year, date.month, date.day).millisecondsSinceEpoch; + + DateSelectType get _type => typeNotifier.value; + + void _setType(DateSelectType type) { + typeNotifier.setType(type); + } +} + +class DateSelectTypeNotifier extends ChangeNotifier { + DateSelectType value = DateSelectType.empty; + DateSelectTypeNotifier(DateSelectType selectType) { + value = selectType; + } + + void setType(DateSelectType type) { + value = type; + notifyListeners(); + } +} diff --git a/tdesign-component/lib/src/components/calendar/td_calendar_header.dart b/tdesign-component/lib/src/components/calendar/td_calendar_header.dart new file mode 100644 index 000000000..589c734c0 --- /dev/null +++ b/tdesign-component/lib/src/components/calendar/td_calendar_header.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +class TDCalendarHeader extends StatelessWidget { + const TDCalendarHeader({ + Key? key, + required this.firstDayOfWeek, + required this.weekdayGap, + required this.padding, + this.weekdayStyle, + required this.weekdayHeight, + this.title, + this.titleStyle, + this.titleWidget, + this.titleMaxLine, + this.titleOverflow, + this.closeBtn = true, + this.closeColor, + this.onClose, + this.onClick, + required this.weekdayNames, + }) : super(key: key); + + final int firstDayOfWeek; + final double weekdayGap; + final double padding; + final TextStyle? weekdayStyle; + final double weekdayHeight; + final String? title; + final TextStyle? titleStyle; + final Widget? titleWidget; + final int? titleMaxLine; + final TextOverflow? titleOverflow; + final bool closeBtn; + final Color? closeColor; + final VoidCallback? onClose; + final List weekdayNames; + final void Function(int index, String week)? onClick; + + List _getWeeks(BuildContext context) { + final ans = []; + var i = firstDayOfWeek % 7; + while (ans.length < 7) { + ans.add(weekdayNames[i]); + i = (i + 1) % 7; + } + return ans; + } + + @override + Widget build(BuildContext context) { + final list = _getWeeks(context); + return Container( + padding: EdgeInsets.fromLTRB(padding, 0, padding, 0), + child: Column( + children: [ + if (title?.isNotEmpty == true || titleWidget != null || closeBtn) + Container( + padding: EdgeInsets.fromLTRB(0, padding, 0, padding), + child: Row( + children: [ + if (closeBtn) const SizedBox(width: 24), + Expanded( + child: Center( + child: titleWidget ?? + TDText( + title, + style: titleStyle, + maxLines: titleMaxLine, + overflow: TextOverflow.ellipsis, + ), + ), + ), + if (closeBtn) + SizedBox( + width: 24, + child: GestureDetector( + child: Icon(TDIcons.close, color: closeColor), + onTap: () { + onClose?.call(); + }, + ), + ), + ], + ), + ), + Row( + children: List.generate(list.length, (index) { + return [ + if (index != 0) SizedBox(width: weekdayGap), + Expanded( + child: GestureDetector( + onTap: () { + onClick?.call(index, list[index]); + }, + child: SizedBox( + height: weekdayHeight, + child: Center( + child: TDText( + list[index], + style: weekdayStyle, + ), + ), + ), + ), + ), + ]; + }).expand((element) => element).toList(), + ), + ], + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/calendar/td_calendar_popup.dart b/tdesign-component/lib/src/components/calendar/td_calendar_popup.dart new file mode 100644 index 000000000..cdf60c4f9 --- /dev/null +++ b/tdesign-component/lib/src/components/calendar/td_calendar_popup.dart @@ -0,0 +1,149 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +typedef CalendarBuilder = Widget Function(BuildContext context); + +enum CalendarTrigger { closeBtn, confirmBtn, overlay } + +/// 单元格组件popup模式 +class TDCalendarPopup { + TDCalendarPopup( + this.context, { + this.top, + this.autoClose = true, + this.confirmBtn, + this.visible, + this.onClose, + this.onConfirm, + this.builder, + this.child, + }) { + if (builder == null && child == null) { + throw FlutterError('[TDCalendarPopup] builder or child must be not null'); + } + if (visible == true) { + show(); + } + } + + /// 上下文 + final BuildContext context; + + /// 距离顶部的距离 + final double? top; + + /// 自动关闭;在点击关闭按钮、确认按钮、遮罩层时自动关闭 + final bool? autoClose; + + /// 自定义确认按钮 + final Widget? confirmBtn; + + /// 默认是否显示日历 + final bool? visible; + + /// 关闭时触发 + final VoidCallback? onClose; + + /// 控件构建器,优先级高于[child] + final CalendarBuilder? builder; + + /// 日历控件 + final TDCalendar? child; + + /// 点击确认按钮时触发 + final void Function(List value)? onConfirm; + + static TDSlidePopupRoute? _calendarPopup; + + /// 当前选中值 + final ValueNotifier> _selected = ValueNotifier>([]); + + bool get _autoClose => autoClose ?? true; + + /// 当前选中值 + List get selected => _selected.value; + + /// 打开日历 + void show() { + if (_calendarPopup != null) { + return; + } + _calendarPopup = TDSlidePopupRoute( + isDismissible: false, + slideTransitionFrom: SlideTransitionFrom.bottom, + modalTop: top, + barrierClick: () { + if (_autoClose) { + close(); + } + }, + builder: (context) { + final childWidget = builder?.call(context) ?? child; + return TDCalendarInherited( + selected: _selected, + usePopup: true, + confirmBtn: confirmBtn, + onClose: _onClose, + onConfirm: _onConfirm, + child: childWidget!, + ); + }, + ); + Navigator.of(context).push(_calendarPopup!).then((_) { + _deleteRouter(); + }); + } + + void _onClose() { + if (_autoClose) { + close(); + } + } + + void _onConfirm() { + onConfirm?.call(_selected.value); + if (_autoClose) { + close(); + } + } + + /// 关闭日历 + void close() { + if (_calendarPopup != null) { + Navigator.of(context).pop(); + // _deleteRouter(); + } + } + + void _deleteRouter() { + _calendarPopup = null; + onClose?.call(); + } +} + +class TDCalendarInherited extends InheritedWidget { + const TDCalendarInherited({ + required Widget child, + this.onClose, + required this.selected, + this.usePopup = true, + this.onConfirm, + this.confirmBtn, + Key? key, + }) : super(child: child, key: key); + + final VoidCallback? onClose; + final ValueNotifier> selected; + final bool? usePopup; + final VoidCallback? onConfirm; + final Widget? confirmBtn; + + @override + bool updateShouldNotify(covariant TDCalendarInherited oldWidget) { + return false; + } + + static TDCalendarInherited? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } +} diff --git a/tdesign-component/lib/src/components/calendar/td_calendar_style.dart b/tdesign-component/lib/src/components/calendar/td_calendar_style.dart new file mode 100644 index 000000000..e2b85327c --- /dev/null +++ b/tdesign-component/lib/src/components/calendar/td_calendar_style.dart @@ -0,0 +1,157 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +/// 日历组件样式 +class TDCalendarStyle { + TDCalendarStyle({ + this.decoration, + this.titleStyle, + this.titleMaxLine, + this.titleCloseColor, + this.weekdayStyle, + this.monthTitleStyle, + this.cellStyle, + this.centreColor, + this.cellDecoration, + this.cellPrefixStyle, + this.cellSuffixStyle, + }); + + BoxDecoration? decoration; + + /// header区域 [TDCalendar.title]的样式 + TextStyle? titleStyle; + + /// header区域 [TDCalendar.title]的行数 + int? titleMaxLine; + + /// header区域 关闭图标的颜色 + Color? titleCloseColor; + + /// header区域 周 文字样式 + TextStyle? weekdayStyle; + + /// body区域 年月文字样式 + TextStyle? monthTitleStyle; + + /// 日期样式 + TextStyle? cellStyle; + + /// 当天日期样式 + TextStyle? todayStyle; + + /// 日期decoration + BoxDecoration? cellDecoration; + + /// 日期范围内背景样式 + Color? centreColor; + + /// 日期前面的字符串的样式 + TextStyle? cellPrefixStyle; + + /// 日期后面的字符串的样式 + TextStyle? cellSuffixStyle; + + /// 日期垂直间距,水平间距为[verticalGap] / 2 + double? verticalGap; + + /// 月与月之间的垂直间距 + double? bodyPadding; + + /// 生成默认样式 + TDCalendarStyle.generateStyle(BuildContext context) { + decoration = BoxDecoration( + color: TDTheme.of(context).whiteColor1, + borderRadius: BorderRadius.vertical( + top: Radius.circular(TDTheme.of(context).radiusExtraLarge), + ), + ); + titleStyle = TextStyle( + fontSize: TDTheme.of(context).fontTitleLarge?.size, + fontWeight: TDTheme.of(context).fontTitleLarge?.fontWeight, + color: TDTheme.of(context).fontGyColor1, + ); + titleMaxLine = 1; + titleCloseColor = titleStyle?.color; + weekdayStyle = TextStyle( + fontSize: TDTheme.of(context).fontTitleSmall?.size, + color: TDTheme.of(context).fontGyColor2, + ); + monthTitleStyle = TextStyle( + fontSize: TDTheme.of(context).fontMarkMedium?.size, + fontWeight: TDTheme.of(context).fontMarkMedium?.fontWeight, + color: TDTheme.of(context).fontGyColor1, + ); + verticalGap = TDTheme.of(context).spacer8; + bodyPadding = TDTheme.of(context).spacer16; + } + + /// 日期样式 + TDCalendarStyle.cellStyle(BuildContext context, DateSelectType? type) { + final radius6 = TDTheme.of(context).radiusDefault; + final defStyle = TextStyle( + fontSize: TDTheme.of(context).fontTitleMedium?.size, + height: TDTheme.of(context).fontTitleMedium?.height, + fontWeight: TDTheme.of(context).fontTitleMedium?.fontWeight, + ); + final prefixStyle = TextStyle( + fontSize: TDTheme.of(context).fontBodyExtraSmall?.size, + height: TDTheme.of(context).fontBodyExtraSmall?.height, + fontWeight: FontWeight.w400, + ); + centreColor = TDTheme.of(context).brandColor1; + switch (type) { + case DateSelectType.empty: + cellStyle = defStyle.copyWith(color: TDTheme.of(context).fontGyColor1); + todayStyle = defStyle.copyWith(color: TDTheme.of(context).brandColor7); + cellPrefixStyle = prefixStyle.copyWith(color: TDTheme.of(context).errorColor6); + cellSuffixStyle = prefixStyle.copyWith(color: TDTheme.of(context).fontGyColor3); + cellDecoration = null; + break; + case DateSelectType.disabled: + cellStyle = defStyle.copyWith(color: TDTheme.of(context).fontGyColor4); + todayStyle = defStyle.copyWith(color: TDTheme.of(context).brandColor3); + cellPrefixStyle = prefixStyle.copyWith(color: TDTheme.of(context).errorColor3); + cellSuffixStyle = prefixStyle.copyWith(color: TDTheme.of(context).fontGyColor4); + cellDecoration = null; + break; + case DateSelectType.selected: + cellStyle = defStyle.copyWith(color: TDTheme.of(context).fontWhColor1); + cellPrefixStyle = prefixStyle.copyWith(color: TDTheme.of(context).fontWhColor1); + cellSuffixStyle = prefixStyle.copyWith(color: TDTheme.of(context).fontWhColor1); + cellDecoration = BoxDecoration( + borderRadius: BorderRadius.circular(radius6), + color: TDTheme.of(context).brandColor7, + ); + break; + case DateSelectType.centre: + cellStyle = defStyle.copyWith(color: TDTheme.of(context).fontGyColor1); + cellPrefixStyle = prefixStyle.copyWith(color: TDTheme.of(context).errorColor6); + cellSuffixStyle = prefixStyle.copyWith(color: TDTheme.of(context).fontGyColor3); + cellDecoration = BoxDecoration( + color: centreColor, + ); + break; + case DateSelectType.start: + cellStyle = defStyle.copyWith(color: TDTheme.of(context).fontWhColor1); + cellPrefixStyle = prefixStyle.copyWith(color: TDTheme.of(context).fontWhColor1); + cellSuffixStyle = prefixStyle.copyWith(color: TDTheme.of(context).fontWhColor1); + cellDecoration = BoxDecoration( + color: TDTheme.of(context).brandColor7, + borderRadius: BorderRadius.horizontal(left: Radius.circular(radius6)), + ); + break; + case DateSelectType.end: + cellStyle = defStyle.copyWith(color: TDTheme.of(context).fontWhColor1); + cellPrefixStyle = prefixStyle.copyWith(color: TDTheme.of(context).fontWhColor1); + cellSuffixStyle = prefixStyle.copyWith(color: TDTheme.of(context).fontWhColor1); + cellDecoration = BoxDecoration( + color: TDTheme.of(context).brandColor7, + borderRadius: BorderRadius.horizontal(right: Radius.circular(radius6)), + ); + break; + default: + break; + } + } +} diff --git a/tdesign-component/lib/src/components/cascader/td_cascader.dart b/tdesign-component/lib/src/components/cascader/td_cascader.dart new file mode 100644 index 000000000..7e03d89f2 --- /dev/null +++ b/tdesign-component/lib/src/components/cascader/td_cascader.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +class TDCascader { + /// 显示多级选择器 + static void showMultiCascader(context, + {String? title, + required List data, + List? initialIndexes, + String? theme, + required onChange, + Duration duration = const Duration(milliseconds: 100), + Color? barrierColor, + double cascaderHeight = 500, + String? initialData, + String? closeText, + bool isLetterSort = false, + List? subTitles, + TDCascaderAction? action, + Function? onClose}) { + showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + barrierColor: + barrierColor ?? TDTheme.of(context).fontGyColor2.withOpacity(0.6), + builder: (context) { + return TDMultiCascader( + title: title, + onClose: onClose, + data: data, + initialIndexes: initialIndexes, + cascaderHeight: cascaderHeight, + initialData: initialData, + onChange: onChange, + closeText: closeText, + action: action, + theme: theme, + isLetterSort: isLetterSort, + subTitles: subTitles); + }); + } +} diff --git a/tdesign-component/lib/src/components/cascader/td_cascader_action.dart b/tdesign-component/lib/src/components/cascader/td_cascader_action.dart new file mode 100644 index 000000000..42bad3d55 --- /dev/null +++ b/tdesign-component/lib/src/components/cascader/td_cascader_action.dart @@ -0,0 +1,22 @@ +import 'package:flutter/cupertino.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; + +/// 级联选择器右上角响应 +class TDCascaderAction { + TDCascaderAction({this.builder, this.text, required this.onConfirm}); + + /// 自定义builder + WidgetBuilder? builder; + + /// 自定义文本 + String? text; + + /// 事件响应 + MultiCascaderCallback onConfirm; + + Widget build(BuildContext context){ + return builder?.call(context) ?? TDText(text ?? context.resource.confirm, textColor: TDTheme.of(context).brandNormalColor, font: TDTheme.of(context).fontTitleMedium,); + } +} \ No newline at end of file diff --git a/tdesign-component/lib/src/components/cascader/td_custom_tab.dart b/tdesign-component/lib/src/components/cascader/td_custom_tab.dart new file mode 100644 index 000000000..d6d2b016f --- /dev/null +++ b/tdesign-component/lib/src/components/cascader/td_custom_tab.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +typedef TapCallback = void Function(int index); + +class TDCustomTab extends StatefulWidget { + final List tabs; + final TapCallback? onTap; + final int? initialIndex; + const TDCustomTab({super.key, required this.tabs, this.onTap,this.initialIndex}); + + @override + State createState() => _TDCustomTabState(); +} + +class _TDCustomTabState extends State { + ScrollController _scrollController = ScrollController(); + int _currentTabIndex = 0; + @override + void initState() { + // TODO: implement initState + super.initState(); + _currentTabIndex=widget.initialIndex??0; + } + + @override + void didUpdateWidget(TDCustomTab oldWidget) { + super.didUpdateWidget(oldWidget); + if(widget.initialIndex!=oldWidget.initialIndex){ + _currentTabIndex=widget.initialIndex!; + } + } + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + controller: _scrollController, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: List.generate(widget.tabs.length, (index) { + return GestureDetector( + onTap:(){ + _onChangeTab(index); + }, + child: Container( + width: 96, + height: 52, + child: Stack( + children: [ + Center( + child: TDText( + widget.tabs[index], + style: TextStyle( + fontSize: 16, + color: _currentTabIndex == index ? TDTheme.of(context).brandNormalColor : Colors.black), + fontWeight: _currentTabIndex == index ?FontWeight.w600:FontWeight.w400, + ), + ), + if (_currentTabIndex == index) + Positioned( + bottom: 0, + left: (96 - 20) / 2, + child: Center( + child: Container( + width: 20, + height: 1.5, + color: TDTheme.of(context).brandNormalColor, + ), + ), + ), + ], + )) + ); + })), + ); + } + + void _onChangeTab(int index) { + if (widget.onTap != null) { + widget.onTap!(index); + } + } +} diff --git a/tdesign-component/lib/src/components/cascader/td_multi_cascader.dart b/tdesign-component/lib/src/components/cascader/td_multi_cascader.dart new file mode 100644 index 000000000..1cff69e86 --- /dev/null +++ b/tdesign-component/lib/src/components/cascader/td_multi_cascader.dart @@ -0,0 +1,579 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; + +typedef MultiCascaderCallback = void Function(List selected); + +class TDMultiCascader extends StatefulWidget { + /// 选择器标题 + final String? title; + + /// 标题样式 + final TextStyle? titleStyle; + + /// 展示风格 可选项:step/tab + final String? theme; + + /// 每级展示的次标题 + final List? subTitles; + + /// 选择器List的视窗高度,默认200 + final double cascaderHeight; + + /// 若为null表示全部从零开始 + final List? initialIndexes; + + /// 选择器的数据源 + final List data; + + /// 初始化数据 + final String? initialData; + + /// 背景颜色 + final Color? backgroundColor; + + /// 顶部圆角 + final double? topRadius; + + /// 是否开启字母排序 + final bool isLetterSort; + + /// 关闭按钮文本 + final String? closeText; + + /// 选择器关闭按钮回调 + final Function? onClose; + + /// 自定义选择器右上角按钮 + final TDCascaderAction? action; + + /// 值发生变更时触发 + final MultiCascaderCallback onChange; + const TDMultiCascader( + {super.key, + this.title, + this.titleStyle, + this.theme, + this.subTitles, + required this.data, + required this.cascaderHeight, + this.initialIndexes, + this.initialData, + this.backgroundColor, + this.topRadius, + this.closeText, + this.isLetterSort = false, + this.onClose, + this.action, + required this.onChange}); + + @override + State createState() => _TDMultiCascaderState(); +} + +class _TDMultiCascaderState extends State with TickerProviderStateMixin { + List _tabListData = []; + + /// 当前tab选中的值 + String? _selectTabValue = ''; + + /// 当前tab索引 + int _currentTabIndex = 0; + + /// tab 层级 + int _level = 0; + + /// 缓存列表数据 + List _listData = []; + + /// tab选中对应的列表数据 + List _selectListData = []; + + final ScrollController _scrollListController = ScrollController(); + + @override + void initState() { + super.initState(); + List.generate(widget.data.length, (index) { + MultiCascaderListModel item = MultiCascaderListModel( + labelFun: ()=>widget.data[index]['label'], + value: widget.data[index]['value'], + segmentValue: widget.data[index]['segmentValue'], + level: 0, + ); + _listData.add(item); + + if (widget.data[index]['children'] != null && widget.data[index]['children'].length > 0) { + _buildRecursiveList(1, widget.data[index]['value'], widget.data[index]['children']); + } + }); + if (widget.isLetterSort) { + _listDataSegmenter(); + } + _initLocation(widget.initialData??''); + _currentTabIndex = _tabListData.length - 1; + _level = _currentTabIndex>0?_currentTabIndex:0; + if(_currentTabIndex>=0){ + _tabListData = _tabListData.reversed.toList(); + _selectTabValue = widget.initialData; + _selectListData = + _listData.where((element) => element.parentValue == _tabListData[_currentTabIndex].parentValue).toList(); + }else{ + _selectListData = _listData.where((element) => element.level == 0).toList(); + _tabListData.add(MultiCascaderListModel( + labelFun: ()=>context.resource.cascadeLabel, + )); + } + } + + @override + Widget build(BuildContext context) { + var maxWidth = MediaQuery.of(context).size.width; + + return Container( + width: maxWidth, + decoration: BoxDecoration( + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(widget.topRadius ?? TDTheme.of(context).radiusExtraLarge), + topRight: Radius.circular(widget.topRadius ?? TDTheme.of(context).radiusExtraLarge), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [_buildTitle(context), _buildTabThemeBox(context), Expanded(child: _buildContentBox(context))], + ), + ); + } + + @override + void didChangeDependencies() { + /// 该方法在开始处必须调用父类的方法 + super.didChangeDependencies(); + Future.delayed(Duration(seconds: 1), () { + List.generate(_selectListData.length, (index) { + if (_selectListData[index].value == _selectTabValue) { + _scrollToListIndex(index); + } + }); + }); + } + + @override + void dispose() { + _scrollListController.dispose(); + super.dispose(); + } + + void _initLocation(String value) { + List list = _listData.where((element) => element.value == value).toList(); + if (list.isNotEmpty) { + _tabListData.add(list[0]); + if (list[0].parentValue != null) { + _initLocation(list[0].parentValue!); + } + } + } + + void _listDataSegmenter() { + _listData.sort((a, b) { + if (a.segmentValue == null || b.segmentValue == null) { + return 0; + } else { + return a.segmentValue!.toLowerCase().compareTo(b.segmentValue!.toLowerCase()); + } + }); + } + + void _buildRecursiveList(int depth, String parentValue, List data) { + List.generate(data.length, (index) { + MultiCascaderListModel item = MultiCascaderListModel( + labelFun: ()=>data[index]['label'], + value: data[index]['value'], + parentValue: parentValue, + segmentValue: data[index]['segmentValue'], + level: depth, + ); + _listData.add(item); + if (data[index]['children'] != null && data[index]['children'].length > 0) { + _buildRecursiveList(depth + 1, data[index]['value'], data[index]['children']); + } + }); + } + + Widget _buildTitle(BuildContext context) { + return Container( + height: 58, + child: Stack( + children: [ + widget.title == null + ? Container() + : Center( + child: TDText( + widget.title, + style: widget.titleStyle ?? + TextStyle( + fontSize: TDTheme.of(context).fontTitleLarge!.size, + fontWeight: FontWeight.w700, + color: TDTheme.of(context).fontGyColor1), + ), + ), + Positioned( + right: 0, + top: 0, + child: GestureDetector( + onTap: () { + if(widget.action != null){ + var result = _tabListData.where((element) => element.label != context.resource.cascadeLabel).toList(); + widget.action?.onConfirm(result); + if(result.isNotEmpty){ + // 返回数据不空,才会自己关闭。如果数据是空的,有业务在回调中自己选择是否关闭 + Navigator.of(context).pop(); + } + } else if (widget.onClose != null) { + widget.onClose!(); + } + }, + child: Container( + height: 58, + alignment: Alignment.center, + child: Padding( + padding: const EdgeInsets.only(left: 2, right: 16), + child: widget.action?.build(context) ?? + (widget.closeText == null + ? Icon( + TDIcons.close, + color: TDTheme.of(context).fontGyColor1, + ) + : TDText( + widget.closeText, + style: TextStyle( + fontSize: TDTheme.of(context) + .fontTitleMedium! + .size, + color: TDTheme.of(context).fontGyColor1), + )), + ), + ))), + ], + ), + ); + } + + Widget _buildTabThemeBox(BuildContext context) { + String them = widget.theme ?? 'step'; + return them == 'step' ? _buildStepBox(context) : _buildTabBox(context); + } + + Widget _buildStepBox(BuildContext context) { + var maxWidth = MediaQuery.of(context).size.width; + return Container( + decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Color.fromRGBO(0, 0, 0, 0.1), width: 0.5))), + padding: EdgeInsets.only(bottom: 11), + width: maxWidth, + child: ListView( + shrinkWrap: true, + children: List.generate(_tabListData.length, (index) { + MultiCascaderListModel tabItem = _tabListData[index]; + return GestureDetector( + onTap: () { + _tabListChange(index); + }, + child: Container( + height: 38, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + // 圆和线 + height: 8, + child: LeftLineWidget( + isCircleFill: tabItem.value == null && tabItem.value != _selectTabValue ? false : true, + isShowTopLine: index == 0 ? false : true, + ), + ), + Expanded( + child: TDText( + '${_tabListData[index].label}', + style: TextStyle( + fontSize: 14, + color: _currentTabIndex == index ? TDTheme.of(context).brandNormalColor : Colors.black), + fontWeight: _currentTabIndex == index ? FontWeight.w600 : FontWeight.w400, + ), + ), + Padding( + padding: const EdgeInsets.only(left: 2, right: 16), + child: Icon( + TDIcons.chevron_right, + color: TDTheme.of(context).fontGyColor3.withOpacity(0.4), + ), + ), + ], + ), + )); + }))); + } + + Widget _buildTabBox(BuildContext context) { + var maxWidth = MediaQuery.of(context).size.width; + return Container( + height: 48, + decoration: BoxDecoration(border: Border(bottom: BorderSide(color: Color.fromRGBO(0, 0, 0, 0.1), width: 0.5))), + width: maxWidth, + child: TDCustomTab( + tabs: List.generate(_tabListData.length, (index) { + return _tabListData[index].label ?? ''; + }), + initialIndex: _currentTabIndex, + onTap: (int index) { + _tabListChange(index); + }, + ), + ); + } + + Widget _buildContentBox(BuildContext context) { + var maxWidth = MediaQuery.of(context).size.width; + return Container( + width: maxWidth, + padding: EdgeInsets.only(left: 16, right: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.subTitles != null) + Container( + height: 50, + padding: EdgeInsets.only( + top: 20, + ), + child: TDText( + widget.subTitles![_level], + style: TextStyle(color: Color.fromRGBO(0, 0, 0, 0.4)), + font: TDTheme.of(context).fontTitleSmall, + ) //, + ), + Expanded( + child: PageView( + scrollDirection: Axis.horizontal, + reverse: false, + controller: PageController(initialPage: 1, keepPage: false), + children: List.generate(1, (index) { + return ListView.builder( + controller: _scrollListController, + itemCount: _selectListData.length, + itemBuilder: (context, index) { + MultiCascaderListModel item = _selectListData[index]; + MultiCascaderListModel preItem = index == 0 ? MultiCascaderListModel() : _selectListData[index - 1]; + return GestureDetector( + onTap: () { + int level = 0; + if (item.level ==0 && _currentTabIndex == 0) { + _tabListData.clear(); + _tabListData.add(MultiCascaderListModel( + labelFun: ()=>context.resource.cascadeLabel, + )); + } + if (item.level != null) { + level = item.level!; + } + + if (widget.subTitles != null && widget.subTitles!.length - 1 > _level) { + _level = level + 1; + } + List isList = _tabListData.where((element) => element.level == item.level).toList(); + if (isList.isNotEmpty) { + _tabListData.removeAt(level); + } + setState(() { + _tabListData.insert(level, item); + _selectTabValue = item.value; + //下一级查询 + _getChildrenListData(level + 1, item.value!); + }); + }, + child: Container( + height: 56, + decoration: BoxDecoration(border: Border.all(color: Colors.transparent)), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + if (item.segmentValue != null) + SizedBox( + width: 32, + child: item.segmentValue != preItem.segmentValue + ? TDText( + '${item.segmentValue}', + font: Font(size: 16, lineHeight: 24), + ) + : null, + ), + TDText( + '${item.label}', + font: Font(size: 16, lineHeight: 24), + ), + ], + ), + ), + if (_selectTabValue == item.value) + Icon( + TDIcons.check, + color: TDTheme.of(context).brandNormalColor, + ) + ], + )), + ); + }, + ); + }), + )) + ], + )); + } + + void _tabListChange(int index) { + MultiCascaderListModel tabItem = _tabListData[index]; + _currentTabIndex = index; + if (tabItem.level != null) { + _selectTabValue = tabItem.value; + } + if (index < _tabListData.length - 1) { + _getFindListData(level: tabItem.level!, value: tabItem.value); + } else { + int cruIndex = index > 0 ? index - 1 : index; + _getFindListData(level: index, parentValue: _tabListData[cruIndex].value); + } + _level = index; + setState(() {}); + } + + void _getChildrenListData(int level, String value) { + //查询层级数据 + var selectLevelData = _listData.where((element) => element.level == (level)&&element.parentValue==value).toList(); + //判断下级是否存在 + if (selectLevelData.isNotEmpty) { + //获取下级数据 + var childList = selectLevelData.where((element) => element.parentValue == value).toList(); + _selectListData = childList; + _currentTabIndex += 1; + } else { + var result = _tabListData.where((element) => element.label != context.resource.cascadeLabel).toList(); + widget.onChange(result); + Navigator.of(context).pop(); + } + } + + /// 查询列表数据 + void _getFindListData({required int level, String? parentValue, String? value}) { + List list = []; + //查询层级数据 + List selectLevelData = _listData.where((element) => element.level == (level)).toList(); + if (selectLevelData.isNotEmpty) { + if (level == 0) { + list = selectLevelData; + } else if (value != null) { + list = selectLevelData.where((element) => element.value == value).toList(); + } else { + list = selectLevelData.where((element) => element.parentValue == parentValue).toList(); + } + _selectListData = list; + } + } + + /// 定位选项在列表中位置 + void _scrollToListIndex(int index) async { + // 计算列表中特定索引的位置 + double scrollTo = index * 56.0; // 每个列表项的高度是56.0 + _scrollListController.animateTo( + scrollTo, + duration: Duration(milliseconds: 1), + curve: Curves.ease, + ); + } +} + +class LeftLineWidget extends StatelessWidget { + /// 是否实心圆 + final bool isCircleFill; + + /// 线条颜色 + final Color? topLineColor; + + /// 是否显示圆圈上方线条 + final bool isShowTopLine; + + const LeftLineWidget({this.isShowTopLine = false, this.topLineColor, this.isCircleFill = false}); + + @override + Widget build(BuildContext context) { + return Container( + margin: EdgeInsets.symmetric(horizontal: 16), + width: 16, + child: CustomPaint( + painter: LeftLinePainter(isShowTopLine: isShowTopLine, topLineColor: topLineColor ?? TDTheme.of(context).brandNormalColor, isCircleFill: isCircleFill), + ), + ); + } +} + +class LeftLinePainter extends CustomPainter { + static const double _topHeight = 16; + + // static const Color _lightColor = Color.fromRGBO(0, 82, 217, 1); + + /// 是否实心圆 + final bool isCircleFill; + + /// 线条颜色 + final Color topLineColor; + + /// 是否显示圆圈上方线条 + final bool isShowTopLine; + + const LeftLinePainter({required this.topLineColor, required this.isShowTopLine, required this.isCircleFill}); + + @override + void paint(Canvas canvas, Size size) { + double lineWidth = 1; + double topHeight = size.height / 2; + double centerX = size.width / 2; + Paint linePain = Paint(); + linePain.color = Colors.transparent; + linePain.strokeWidth = lineWidth; + linePain.strokeCap = StrokeCap.square; + canvas.drawLine(Offset(centerX, 0), Offset(centerX, topHeight), linePain); + Paint circlePaint = Paint(); + circlePaint.color = topLineColor; + circlePaint.strokeWidth = 1; + circlePaint.style = isCircleFill ? PaintingStyle.fill : PaintingStyle.stroke; + linePain.color = isShowTopLine ? (topLineColor) : Colors.transparent; + canvas.drawLine(Offset(centerX, -size.height), Offset(centerX, -size.height - _topHeight), linePain); + canvas.drawCircle(Offset(centerX, topHeight), centerX * 0.5, circlePaint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return true; + } +} + +class MultiCascaderListModel { + String? Function()? labelFun; + String? get label => labelFun?.call(); + + String? value; + + /// 父级值 + String? parentValue; + + /// 分组值 + String? segmentValue; + + int? level; + MultiCascaderListModel({this.labelFun, this.value, this.parentValue, this.level, this.segmentValue}); +} diff --git a/tdesign-component/lib/src/components/cell/td_cell.dart b/tdesign-component/lib/src/components/cell/td_cell.dart new file mode 100644 index 000000000..008d8ee49 --- /dev/null +++ b/tdesign-component/lib/src/components/cell/td_cell.dart @@ -0,0 +1,275 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../theme/td_spacers.dart'; +import '../../theme/td_theme.dart'; +import '../icon/td_icons.dart'; +import '../swipe_cell/td_swipe_cell_inherited.dart'; +import '../text/td_text.dart'; +import 'td_cell_inherited.dart'; +import 'td_cell_style.dart'; + +typedef TDCellClick = void Function(TDCell cell); + +enum TDCellAlign { top, middle, bottom } + +/// 单元格组件 +class TDCell extends StatefulWidget { + const TDCell({ + Key? key, + this.align = TDCellAlign.middle, + this.arrow = false, + this.bordered = true, + this.description, + this.descriptionWidget, + this.hover = true, + this.image, + this.imageSize, + this.imageWidget, + this.leftIcon, + this.leftIconWidget, + this.note, + this.noteWidget, + this.required = false, + this.title, + this.titleWidget, + this.onClick, + this.onLongPress, + this.style, + this.rightIcon, + this.rightIconWidget, + this.disabled = false, + this.imageCircle = 50, + this.showBottomBorder = false, + this.height, + }) : super(key: key); + + /// 内容的对齐方式,默认居中对齐。可选项:top/middle/bottom + final TDCellAlign? align; + + /// 是否显示右侧箭头 + final bool? arrow; + + /// 是否显示下边框,仅在TDCellGroup组件下起作用 + final bool? bordered; + + /// 下方内容描述文字 + final String? description; + + /// 下方内容描述组件 + final Widget? descriptionWidget; + + /// 是否开启点击反馈 + final bool? hover; + + /// 主图 + final ImageProvider? image; + + /// 主图尺寸 + final double? imageSize; + + /// 主图圆角,默认50(圆形) + final double? imageCircle; + + /// 主图组件 + final Widget? imageWidget; + + /// 左侧图标,出现在单元格标题的左侧 + final IconData? leftIcon; + + /// 左侧图标组件 + final Widget? leftIconWidget; + + /// 和标题同行的说明文字 + final String? note; + + /// 说明文字组件 + final Widget? noteWidget; + + /// 是否显示表单必填星号 + final bool? required; + + /// 最右侧图标 + final IconData? rightIcon; + + /// 最右侧图标组件 + final Widget? rightIconWidget; + + /// 标题 + final String? title; + + /// 标题组件 + final Widget? titleWidget; + + /// 点击事件 + final TDCellClick? onClick; + + /// 长按事件 + final TDCellClick? onLongPress; + + /// 自定义样式 + final TDCellStyle? style; + + /// 禁用 + final bool? disabled; + + /// 是否显示下边框(建议TDCellGroup组件下false,避免与bordered重叠) + final bool? showBottomBorder; + + /// 高度 + final double? height; + + @override + _TDCellState createState() => _TDCellState(); +} + +class _TDCellState extends State { + var _status = 'default'; + + @override + Widget build(BuildContext context) { + var style = widget.style ?? TDCellInherited.of(context)?.style ?? TDCellStyle.cellStyle(context); + var crossAxisAlignment = _getAlign(); + var color = _status == 'default' ? style.backgroundColor : style.clickBackgroundColor; + var border; + if(widget.showBottomBorder!) { + border = Border(bottom: BorderSide(width: 1, color: style.borderedColor ?? TDTheme.of(context).grayColor3)); + } + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (widget.onClick != null && !(widget.disabled ?? false)) { + widget.onClick!(widget); + } + TDSwipeCellInherited.of(context)?.cellClick(); + }, + onLongPress: widget.onLongPress != null ? () { + if (!(widget.disabled ?? false)) { + widget.onLongPress!(widget); + } + } : null, + onTapDown: (details) { + _setStatus('active', 0); + }, + onTapUp: (details) { + _setStatus('default', 100); + }, + onTapCancel: () { + _setStatus('default', 0); + }, + child: Container( + height: widget.height, + padding: style.padding, + decoration: BoxDecoration(color: color, border: border), + child: Row( + crossAxisAlignment: crossAxisAlignment, + children: [ + ..._getImage(), + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.leftIcon != null || widget.leftIconWidget != null) ...[ + widget.leftIconWidget ?? Icon(widget.leftIcon, size: 24, color: style.leftIconColor), + SizedBox(width: TDTheme.of(context).spacer12), + ], + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + if (widget.titleWidget != null) + Flexible(child: widget.titleWidget!) + else if (widget.title?.isNotEmpty == true) + Flexible(child: TDText(widget.title!, style: style.titleStyle)), + if (widget.required ?? false) TDText(' *', style: style.requiredStyle), + ], + ), + if ((widget.titleWidget != null || widget.title != null) && + (widget.descriptionWidget != null || widget.description?.isNotEmpty == true)) + SizedBox(height: TDTheme.of(context).spacer4), + if (widget.descriptionWidget != null) + widget.descriptionWidget! + else if (widget.description?.isNotEmpty == true) + TDText(widget.description!, style: style.descriptionStyle), + ], + ), + ), + ], + ), + ), + Wrap( + spacing: TDTheme.of(context).spacer4, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + if (widget.noteWidget != null) + widget.noteWidget! + else if (widget.note?.isNotEmpty == true) + TDText(widget.note!, style: style.noteStyle), + if (widget.rightIconWidget != null) + widget.rightIconWidget! + else if (widget.rightIcon != null) + Icon(widget.rightIcon, size: 24, color: style.rightIconColor), + if (widget.arrow ?? false) Icon(TDIcons.chevron_right, size: 24, color: style.arrowColor), + ], + ), + ], + ), + ), + ); + } + + CrossAxisAlignment _getAlign() { + switch (widget.align) { + case TDCellAlign.top: + return CrossAxisAlignment.start; + case TDCellAlign.middle: + return CrossAxisAlignment.center; + case TDCellAlign.bottom: + return CrossAxisAlignment.end; + default: + return CrossAxisAlignment.center; + } + } + + void _setStatus(String status, int milliseconds) { + if ((widget.disabled ?? false) || !(widget.hover ?? true)) { + return; + } + if (milliseconds == 0) { + setState(() { + _status = status; + }); + return; + } + Future.delayed(Duration(milliseconds: milliseconds), () { + setState(() { + _status = status; + }); + }); + } + + List _getImage() { + var imageSize = widget.imageSize ?? 48; + var list = []; + if (widget.imageWidget != null) { + list.add(widget.imageWidget!); + } else if (widget.image != null) { + list.add(ClipRRect( + borderRadius: BorderRadius.circular(widget.imageCircle ?? 50), + child: Image( + image: widget.image!, + width: imageSize, + height: imageSize, + fit: BoxFit.cover, + ), + )); + } + if (list.isEmpty) { + return list; + } + list.add(SizedBox(width: TDTheme.of(context).spacer12)); + return list; + } +} diff --git a/tdesign-component/lib/src/components/cell/td_cell_group.dart b/tdesign-component/lib/src/components/cell/td_cell_group.dart new file mode 100644 index 000000000..6c216f2f9 --- /dev/null +++ b/tdesign-component/lib/src/components/cell/td_cell_group.dart @@ -0,0 +1,151 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import 'td_cell_inherited.dart'; + +typedef CellBuilder = Widget Function( + BuildContext context, TDCell cell, int index); + +enum TDCellGroupTheme { defaultTheme, cardTheme } + +/// 单元格组组件 +class TDCellGroup extends StatefulWidget { + const TDCellGroup({ + Key? key, + this.bordered = false, + this.theme = TDCellGroupTheme.defaultTheme, + this.title, + required this.cells, + this.builder, + this.style, + this.titleWidget, + this.scrollable = false, + this.isShowLastBordered = false, + }) : super(key: key); + + /// 是否显示组边框 + final bool? bordered; + + /// 单元格组风格。可选项:default/card + final TDCellGroupTheme? theme; + + /// 单元格组标题 + final String? title; + + /// 单元格组标题组件 + final Widget? titleWidget; + + /// 单元格列表 + final List cells; + + /// cell构建器,可自定义cell父组件,如Dismissible + final CellBuilder? builder; + + /// 自定义样式 + final TDCellStyle? style; + + /// 可滚动 + final bool? scrollable; + + /// 是否显示最后一个cell的下边框 + final bool? isShowLastBordered; + + @override + _TDCellGroupState createState() => _TDCellGroupState(); +} + +class _TDCellGroupState extends State { + @override + Widget build(BuildContext context) { + var style = widget.style ?? TDCellStyle.cellStyle(context); + var itemCount = widget.cells.length; + var radius = _getBorderRadius(style); + return TDCellInherited( + style: style, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.title != null || widget.titleWidget != null) + Container( + width: double.infinity, + color: style.titleBackgroundColor, + padding: style.titlePadding, + child: widget.titleWidget ?? TDText(widget.title!, style: style.groupTitleStyle), + ), + Flexible( + child: Container( + padding: widget.theme == TDCellGroupTheme.cardTheme + ? style.cardPadding + : EdgeInsets.zero, + decoration: BoxDecoration( + border: _getBordered(style), borderRadius: radius), + child: ClipRRect( + borderRadius: radius, + child: ListView.separated( + padding: EdgeInsets.zero, + shrinkWrap: widget.scrollable == false, // 设置为true以避免无限制地增长 + physics: widget.scrollable == false + ? const NeverScrollableScrollPhysics() + : null, // 禁用ListView的滚动 + itemCount: itemCount, + itemBuilder: (context, index) { + final item = widget.cells[index]; + final cell = widget.builder == null + ? item + : widget.builder!(context, item, index); + if (itemCount - 1 == index && + (widget.isShowLastBordered ?? false)) { + return Column(children: [cell, _borderWidget(style)]); + } + return cell; + }, + separatorBuilder: (context, index) { + if (!(widget.cells[index].bordered ?? true)) { + return const SizedBox.shrink(); + } + return _borderWidget(style); + }, + ), + ), + ), + ), + ], + ), + ); + } + + BoxBorder? _getBordered(TDCellStyle style) { + if (!(widget.bordered ?? false)) { + return null; + } + var color = style.groupBorderedColor ?? TDTheme.of(context).grayColor3; + return Border.all( + color: color, + width: 1, + ); + } + + BorderRadiusGeometry _getBorderRadius(TDCellStyle style) { + if (widget.theme == TDCellGroupTheme.cardTheme) { + return style.cardBorderRadius ?? BorderRadius.zero; + } + return BorderRadius.zero; + } + + Widget _borderWidget(TDCellStyle style) { + return Row( + children: [ + Container( + height: 0.5, + width: TDTheme.of(context).spacer16, + color: style.backgroundColor), + Expanded( + child: Container( + height: 0.5, + color: style.borderedColor ?? TDTheme.of(context).grayColor3), + ), + ], + ); + } +} diff --git a/tdesign-component/lib/src/components/cell/td_cell_inherited.dart b/tdesign-component/lib/src/components/cell/td_cell_inherited.dart new file mode 100644 index 000000000..94ad9d90b --- /dev/null +++ b/tdesign-component/lib/src/components/cell/td_cell_inherited.dart @@ -0,0 +1,19 @@ +import 'package:flutter/cupertino.dart'; + +import 'td_cell_style.dart'; + +class TDCellInherited extends InheritedWidget { + const TDCellInherited({required Widget child, required this.style, Key? key}) + : super(child: child, key: key); + + final TDCellStyle style; + + @override + bool updateShouldNotify(covariant TDCellInherited oldWidget) { + return true; + } + + static TDCellInherited? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } +} diff --git a/tdesign-component/lib/src/components/cell/td_cell_style.dart b/tdesign-component/lib/src/components/cell/td_cell_style.dart new file mode 100644 index 000000000..ec43d2d52 --- /dev/null +++ b/tdesign-component/lib/src/components/cell/td_cell_style.dart @@ -0,0 +1,132 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +/// 单元格组件样式 +class TDCellStyle { + TDCellStyle({ + this.context, + this.leftIconColor, + this.rightIconColor, + this.titleStyle, + this.requiredStyle, + this.descriptionStyle, + this.noteStyle, + this.arrowColor, + this.borderedColor, + this.groupBorderedColor, + this.backgroundColor, + this.clickBackgroundColor, + this.groupTitleStyle, + this.padding, + this.cardBorderRadius, + this.cardPadding, + this.titlePadding, + this.titleBackgroundColor, + }) { + if (context != null) { + defaultStyle(context!); + } + } + + /// 传递context,会生成默认样式 + BuildContext? context; + + /// 左侧图标颜色 + Color? leftIconColor; + + /// 右侧图标颜色 + Color? rightIconColor; + + /// 标题文字样式 + TextStyle? titleStyle; + + /// 必填星号文字样式 + TextStyle? requiredStyle; + + /// 内容描述文字样式 + TextStyle? descriptionStyle; + + /// 说明文字样式 + TextStyle? noteStyle; + + /// 箭头颜色 + Color? arrowColor; + + /// 单元格边框颜色 + Color? borderedColor; + + /// 单元格组边框颜色 + Color? groupBorderedColor; + + /// 默认状态背景颜色 + Color? backgroundColor; + + /// 点击状态背景颜色 + Color? clickBackgroundColor; + + /// 单元组标题文字样式 + TextStyle? groupTitleStyle; + + /// 单元格内边距 + EdgeInsets? padding; + + /// 卡片模式边框圆角 + BorderRadius? cardBorderRadius; + + /// 卡片模式内边距 + EdgeInsets? cardPadding; + + /// 单元格组标题内边距 + EdgeInsets? titlePadding; + + /// 单元格组标题背景颜色 + Color? titleBackgroundColor; + + /// 生成单元格默认样式 + TDCellStyle.cellStyle(BuildContext context) { + defaultStyle(context); + } + + defaultStyle(BuildContext context) { + backgroundColor = Colors.white; + clickBackgroundColor = TDTheme.of(context).grayColor1; + leftIconColor = TDTheme.of(context).brandColor7; + rightIconColor = TDTheme.of(context).brandColor7; + titleStyle = TextStyle( + color: TDTheme.of(context).fontGyColor1, + fontSize: TDTheme.of(context).fontBodyLarge?.size ?? 16, + height: TDTheme.of(context).fontBodyLarge?.height ?? 24, + fontWeight: FontWeight.w400, + ); + requiredStyle = titleStyle!.copyWith(color: TDTheme.of(context).errorColor6); + descriptionStyle = TextStyle( + color: TDTheme.of(context).fontGyColor2, + fontSize: TDTheme.of(context).fontBodyMedium?.size ?? 14, + height: TDTheme.of(context).fontBodyMedium?.height ?? 22, + fontWeight: FontWeight.w400, + ); + noteStyle = titleStyle!.copyWith(color: TDTheme.of(context).fontGyColor3); + arrowColor = TDTheme.of(context).fontGyColor3; + + groupBorderedColor = TDTheme.of(context).grayColor3; + borderedColor = TDTheme.of(context).grayColor3; + groupTitleStyle = TextStyle( + color: TDTheme.of(context).fontGyColor1, + fontSize: TDTheme.of(context).fontTitleLarge?.size ?? 18, + height: TDTheme.of(context).fontTitleLarge?.height ?? 26, + fontWeight: TDTheme.of(context).fontTitleLarge?.fontWeight ?? FontWeight.w600, + ); + + padding = EdgeInsets.all(TDTheme.of(context).spacer16); + cardBorderRadius = BorderRadius.all(Radius.circular(TDTheme.of(context).radiusLarge)); + cardPadding = EdgeInsets.only(left: TDTheme.of(context).spacer16, right: TDTheme.of(context).spacer16); + titlePadding = EdgeInsets.only( + left: TDTheme.of(context).spacer16, + right: TDTheme.of(context).spacer16, + top: TDTheme.of(context).spacer24, + bottom: TDTheme.of(context).spacer8, + ); + titleBackgroundColor = Colors.transparent; + } +} diff --git a/tdesign-component/lib/src/components/checkbox/td_check_box.dart b/tdesign-component/lib/src/components/checkbox/td_check_box.dart new file mode 100644 index 000000000..0db0f2e74 --- /dev/null +++ b/tdesign-component/lib/src/components/checkbox/td_check_box.dart @@ -0,0 +1,559 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +import '../../util/auto_size.dart'; + +/// +/// 选择框的样式 +/// +enum TDCheckboxStyle { + circle, // 圆形 + square, // 方形 + check, // 无背景勾选样式 +} + +/// +/// 内容相对icon的位置,上、下、左、右,默认内容在icon的右边 +/// +enum TDContentDirection { + left, // content在icon的左边 + right, // content在icon的右边 +} + +enum TDCheckBoxSize { + large, // 大 高度56 + small, // 小 高度48 +} + +/// +/// 自定义Icon +/// +typedef IconBuilder = Widget? Function(BuildContext context, bool checked); + +/// +/// 自定义Content +/// +typedef ContentBuilder = Widget Function(BuildContext context, bool checked, String? content); + +typedef OnCheckValueChanged = void Function(bool selected); + +/// +/// 复选框组件。 +/// +/// FuiCheckbox支持3种内置样式的的复选框,还支持各种自定义样式,除了提供勾选之外还提供了内 +/// 容选项,内容包含一个主标题和副标题,并且支持完全自定义内容,支持指定内容的方向等等 +/// +/// +class TDCheckbox extends StatefulWidget { + const TDCheckbox( + {this.id, + Key? key, + this.title, + this.subTitle, + this.titleFont, + this.subTitleFont, + this.enable = true, + this.checked = false, + this.titleMaxLine, + this.subTitleMaxLine = 1, + this.customIconBuilder, + this.customContentBuilder, + this.insetSpacing = 16, + this.style, + this.spacing, + this.backgroundColor, + this.selectColor, + this.disableColor, + this.size = TDCheckBoxSize.small, + this.cardMode = false, + this.showDivider = true, + this.contentDirection = TDContentDirection.right, + this.onCheckBoxChanged, + this.titleColor, + this.subTitleColor, + this.checkBoxLeftSpace, + this.customSpace}) + : super(key: key); + + /// id + /// 当FuiCheckBox嵌入到FuiCheckBoxGroup内时,这个值需要赋值,否则不会被纳入Group管理 + final String? id; + + /// 文本 + final String? title; + + /// 标题字体大小 + final Font? titleFont; + + /// 辅助文字 + final String? subTitle; + + /// 副标题字体大小 + final Font? subTitleFont; + + /// 不可用 + final bool enable; + + /// 选中状态。默认为`false` + /// 当FuiCheckBox嵌入到FuiCheckBoxGroup的时候,这个值表示初始状态,后续的状态会由Group管理 + final bool checked; + + /// 标题的行数 + final int? titleMaxLine; + + /// 辅助文字的行数 + final int? subTitleMaxLine; + + /// 文字和非图标侧的距离 + final double? insetSpacing; + + /// icon和文字的距离 + final double? spacing; + + /// 复选框样式:圆形或方形 + final TDCheckboxStyle? style; + + /// 复选框大小 + final TDCheckBoxSize size; + + /// 展示为卡片模式 + final bool cardMode; + + /// 是否展示分割线 + final bool showDivider; + + /// 文字相对icon的方位 + final TDContentDirection contentDirection; + + /// 切换监听 + final OnCheckValueChanged? onCheckBoxChanged; + + /// 自定义Checkbox显示样式 + final IconBuilder? customIconBuilder; + + /// 完全自定义内容 + final ContentBuilder? customContentBuilder; + + /// 背景颜色 + final Color? backgroundColor; + + /// 选择颜色 + final Color? selectColor; + + /// 禁用选择颜色 + final Color? disableColor; + + /// 标题文字颜色 + final Color? titleColor; + + /// 副标题文字颜色 + final Color? subTitleColor; + + /// 选项框左侧间距 + final double? checkBoxLeftSpace; + + /// 自定义组件间距 + final EdgeInsetsGeometry? customSpace; + @override + State createState() => TDCheckboxState(); + + /// 默认的checkBox icon + Widget buildDefaultIcon(BuildContext context, TDCheckboxGroupState? groupState, bool isChecked) { + if (cardMode == true) { + return Container(); + } + Widget current; + var size = 24.0; + final style = this.style ?? groupState?.widget.style ?? TDCheckboxStyle.circle; + final theme = TDTheme.of(context); + final deSelectedColor = style == TDCheckboxStyle.check ? Colors.transparent : theme.grayColor4; + current = Icon( + style == TDCheckboxStyle.circle + ? isChecked + ? TDIcons.check_circle_filled + : TDIcons.circle + : style == TDCheckboxStyle.square + ? isChecked + ? TDIcons.check_rectangle_filled + : TDIcons.rectangle + : isChecked + ? TDIcons.check + : TDIcons.check, + size: size, + color: !enable + ? (isChecked ? (disableColor ?? theme.brandDisabledColor) : deSelectedColor) + : isChecked + ? selectColor ?? theme.brandNormalColor + : deSelectedColor, + ); + return current; + } +} + +class TDCheckboxState extends State { + bool checked = false; + bool _pressed = false; + + /// 不可取消勾选,在radioButton的严格模式下,只能切换不能取消勾选 + bool canNotCancel = false; + + @override + void initState() { + checked = widget.checked; + super.initState(); + } + + @override + void didUpdateWidget(TDCheckbox oldWidget) { + checked = widget.checked; + super.didUpdateWidget(oldWidget); + } + + double _spacing(TDCheckboxGroupState? groupState) { + return widget.spacing ?? groupState?.widget.spacing ?? 8; + } + + EdgeInsets _getPadding(TDCheckBoxSize size) { + if (widget.cardMode) { + return const EdgeInsets.only(top: 16); + } + switch (size) { + case TDCheckBoxSize.small: + return const EdgeInsets.only(top: 12, bottom: 12); + case TDCheckBoxSize.large: + return const EdgeInsets.only(top: 16, bottom: 16); + } + } + + @override + Widget build(BuildContext context) { + // 检查是否包含在FuiCheckBoxGroup内,如果是的话,状态由Group管理 + final groupState = TDCheckboxGroupInherited.of(context)?.state; + final id = widget.id; + // 只有设置了id的CheckBox才会纳入Group管理 + if (groupState != null && id != null) { + // CheckBox嵌入在CheckBoxGroup的勾选状态的获取优先级: + // 1.CheckBoxGroup里设置的checkedIds包含当前id + // 2.没有checkedIds属性,且没有勾选过,使用当前CheckedBox设置的checked属性 + // 3.用户勾选之后的状态 + checked = groupState.getCheckBoxStateById(id, checked); + } + + // 构建icon + var icon = _buildCheckboxIcon(context, groupState, checked); + + // 内容 + var content = _buildContent(context, groupState, checked); + + if (icon == null && content == null) { + throw Exception('Icon and content cannot both be null!'); + } + + Widget? current; + + if (icon == null) { + current = content!; + } else { + current = icon; + + if (content != null) { + final spacing = _spacing(groupState); + var contentDirection = groupState?.widget.contentDirection ?? widget.contentDirection; + switch (contentDirection) { + case TDContentDirection.left: + current = Stack( + alignment: Alignment.bottomCenter, + children: [ + Padding( + padding:widget.customSpace??_getPadding(widget.size), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Padding( + padding: EdgeInsets.only(left: widget.insetSpacing ?? 16), + child: content, + )), + SizedBox( + width: spacing, + ), + Padding( + padding: const EdgeInsets.only(right: 16), + child: icon, + ), + ], + ), + Visibility( + visible: widget.subTitle != null && widget.subTitle != '', + child: Padding( + padding: EdgeInsets.only(left: widget.insetSpacing ?? 16, right: 16), + child: TDText(widget.subTitle ?? '', + maxLines: widget.subTitleMaxLine, + overflow: TextOverflow.ellipsis, + textColor: widget.enable + ? (widget.subTitleColor ?? TDTheme.of(context).fontGyColor3) + : TDTheme.of(context).fontGyColor4, + font: TDTheme.of(context).fontBodyMedium), + ), + ) + ], + ), + ), + Visibility( + visible: !widget.cardMode && widget.showDivider, + child: const TDDivider( + margin: EdgeInsets.only(left: 16), + )) + ], + ); + break; + case TDContentDirection.right: + current = Stack( + alignment: Alignment.bottomCenter, + children: [ + Padding( + padding:widget.customSpace?? _getPadding(widget.size), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: EdgeInsets.only(left: widget.checkBoxLeftSpace ?? 16), + child: icon, + ), + SizedBox( + width: widget.cardMode ? 0 : spacing, + ), + Expanded( + child: Padding( + padding: EdgeInsets.only(right: widget.insetSpacing ?? 16), + child: content, + )), + ], + ), + Visibility( + visible: widget.subTitle != null && widget.subTitle != '', + child: Padding( + padding: EdgeInsets.only( + top: widget.cardMode ? 4.scale : 0, + left: widget.cardMode ? 16 : 48, + right: widget.insetSpacing ?? 16), + child: TDText(widget.subTitle ?? '', + maxLines: widget.subTitleMaxLine, + overflow: TextOverflow.ellipsis, + textColor: widget.enable + ? (widget.subTitleColor ?? TDTheme.of(context).fontGyColor3) + : TDTheme.of(context).fontGyColor4, + font: widget.subTitleFont ?? TDTheme.of(context).fontBodyMedium), + ), + ) + ], + ), + ), + Visibility( + visible: !widget.cardMode && widget.showDivider, + child: const TDDivider( + margin: EdgeInsets.only(left: 48), + )) + ], + ); + break; + } + } + } + + if (!(canNotCancel && checked)) { + if (_pressed) { + // 点击效果 + current = Opacity( + opacity: 0.68, + child: current, + ); + } + + // 开关样式的话自己会处理点击事件 + current = GestureDetector( + behavior: HitTestBehavior.translucent, + onTapDown: (detail) { + _pressState(true); + }, + onTapUp: (detail) { + _pressState(false); + }, + onTapCancel: () { + _pressState(false); + }, + onTap: () { + onValueChange(id, !checked, groupState); + }, + child: current, + ); + } + + return Container( + clipBehavior: widget.cardMode ? Clip.hardEdge : Clip.none, + decoration: BoxDecoration( + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + border: widget.cardMode + ? checked + ? Border.all(width: 1.5, color: widget.selectColor ?? TDTheme.of(context).brandNormalColor) + : Border.all(width: 1.5, color: Colors.transparent) + : null, + borderRadius: widget.cardMode ? const BorderRadius.all(Radius.circular(6)) : null), + child: Stack( + children: [ + current, + Positioned( + top: 0, + left: 0, + child: Visibility( + visible: widget.cardMode && checked, + child: RadioCornerIcon( + length: 28, + radius: 4, + selectColor: widget.selectColor, + ))), + ], + ), + ); + } + + /// 点击效果 + void _pressState(bool pressed) { + if (!widget.enable) { + return; + } + _pressed = pressed; + setState(() {}); + } + + /// 选中状态的变化 + void onValueChange( + String? id, + bool value, + TDCheckboxGroupState? groupState, + ) { + if (!widget.enable) { + return; + } + setState(() { + checked = value; + if (groupState != null && id != null) { + groupState.toggle(id, checked); + } + widget.onCheckBoxChanged?.call(checked); + }); + } + + /// + /// 构建选择框边上的文本内容 + /// + Widget? _buildContent( + BuildContext context, + TDCheckboxGroupState? groupState, + bool checked, + ) { + final title = widget.title; + final customContent = widget.customContentBuilder ?? groupState?.widget.customContentBuilder; + + var content = customContent?.call(context, checked, title); + if (content == null) { + if (title != null || customContent != null && title != null) { + content = TDText(title, + maxLines: widget.titleMaxLine ?? groupState?.widget.titleMaxLine, + overflow: TextOverflow.ellipsis, + textColor: widget.enable + ? (widget.titleColor ?? TDTheme.of(context).fontGyColor1) + : TDTheme.of(context).fontGyColor4, + font: widget.titleFont ?? + TDTheme.of(context) + .fontBodyLarge); // TODO custom fontSize https://github.com/Tencent/tdesign-flutter/issues/66 + } + } + return content; + } + + /// 构建icon + Widget? _buildCheckboxIcon(BuildContext context, TDCheckboxGroupState? groupState, bool isCheck) { + final iconBuilder = widget.customIconBuilder ?? groupState?.widget.customIconBuilder; + if (iconBuilder != null) { + return iconBuilder.call(context, isCheck); + } + return widget.buildDefaultIcon(context, groupState, isCheck); + } +} + +class RadioCornerIcon extends StatelessWidget { + final double length; + final double radius; + final Color? selectColor; + + const RadioCornerIcon({Key? key, required this.length, required this.radius, required this.selectColor}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: length, + height: length, + child: Stack( + alignment: Alignment.topLeft, + children: [ + CustomPaint( + painter: RadioCorner( + length: length, radius: radius, fillColor: selectColor ?? TDTheme.of(context).brandNormalColor), + ), + const Positioned( + top: 3, + left: 2, + child: Icon( + TDIcons.check, + size: 14, + color: Colors.white, + )) + ], + ), + ); + } +} + +class RadioCorner extends CustomPainter { + final double length; + final double radius; + final Color fillColor; + + RadioCorner({required this.length, required this.radius, required this.fillColor}); + + @override + void paint(Canvas canvas, Size size) { + var paint = Paint() + ..isAntiAlias = true + ..strokeWidth = 1 + ..color = fillColor + ..style = PaintingStyle.fill; + var rect = Rect.fromCircle(center: Offset(radius, radius), radius: radius); + var pi = 3.1415; + var path = Path(); + path.moveTo(0, radius); + path.addArc( + rect, + 180 * (pi / 180.0), + 90 * (pi / 180.0), + ); + path.moveTo(radius, 0); + path.lineTo(length, 0); + path.lineTo(0, length); + path.lineTo(0, radius); + canvas.drawPath(path, paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} diff --git a/tdesign-component/lib/src/components/checkbox/td_check_box_group.dart b/tdesign-component/lib/src/components/checkbox/td_check_box_group.dart new file mode 100644 index 000000000..08ba10b69 --- /dev/null +++ b/tdesign-component/lib/src/components/checkbox/td_check_box_group.dart @@ -0,0 +1,455 @@ +import 'package:flutter/cupertino.dart'; + +import '../../util/auto_size.dart'; +import '../../util/map_ext.dart'; +import 'td_check_box.dart'; + +/// +/// CheckBoxGroup变化监听器 +/// +typedef OnGroupChange = void Function(List checkedIds); + +/// +/// 控制CheckBoxGroup +/// +class TDCheckboxGroupController { + TDCheckboxGroupState? _state; + + /// + /// 选择全部 + /// 这个方法会忽略最大可够选数 + /// + void toggleAll(bool check) { + _state?.toggleAll(check); + } + + /// + /// 反选 + /// + void reverseAll() { + _state?._reverseAll(); + } + + /// + /// 选中某一个选项 + /// + void toggle(String id, bool check) { + _state?.toggle(id, check, true); + } + + /// + /// 打勾的选项 + /// + List allChecked() { + return _state?.checkBoxStates.where((k, v) => v).keys.toList() ?? []; + } + + /// + /// 某一项的选中状态 + /// + bool checked(String id) { + var list = allChecked(); + return list.contains(id); + } +} + +/// +/// CheckBox组,可以通过控制器控制组内的多个CheckBox的选择状态 +/// +/// child的属性可以是任意包含TDCheckBox的容器组件,例如: +/// ```dart +/// TDCheckboxGroup( +/// child: Row( +/// children: [ +/// TDCheckBox(), +/// Column( +/// children: [ +/// TDCheckBox() +/// ... +/// ] +/// ) +/// ... +/// ] +/// ) +/// ) +/// ``` +/// +/// +class TDCheckboxGroup extends StatefulWidget { + + const TDCheckboxGroup( + {required this.child, + Key? key, + this.onChangeGroup, + this.controller, + this.checkedIds, + this.maxChecked, + this.titleMaxLine, + this.customContentBuilder, + this.contentDirection, + this.style, + this.spacing, + this.customIconBuilder, + this.onOverloadChecked}) : super(key: key); + + /// + /// 可以是任意包含TDCheckBox的容器,比如: + /// ``` + /// Row( + /// children: [ + /// TDCheckBox(), + /// TDCheckBox(), + /// ... + /// ] + /// ) + /// ``` + /// + final Widget child; + + /// 状态变化监听器 + final OnGroupChange? onChangeGroup; + + /// 可以通过控制器操作勾选状态 + final TDCheckboxGroupController? controller; + + /// 最多可以勾选多少 + final int? maxChecked; + + /// 勾选的CheckBox id列表 + final List? checkedIds; + + /// 超过最大可勾选的个数 + final VoidCallback? onOverloadChecked; + + /// CheckBox标题的行数 + final int? titleMaxLine; + + + /// CheckBox完全自定义内容 + final ContentBuilder? customContentBuilder; + + /// CheckBoxicon和文字的距离 + final double? spacing; + + /// CheckBox复选框样式:圆形或方形 + final TDCheckboxStyle? style; + + /// 文字相对icon的方位 + final TDContentDirection? contentDirection; + + /// 自定义选择icon的样式 + final IconBuilder? customIconBuilder; + + @override + State createState() { + return TDCheckboxGroupState(); + } +} + + +class TDCheckboxGroupState extends State { + /// + /// 管理所有子CheckBox的状态 + /// + Map checkBoxStates = {}; + + @override + void initState() { + super.initState(); + // 如果有controller的话,把state设置给controller + widget.controller?._state = this; + + _syncCheckState(widget.checkedIds); + } + + /// 把group中配置的默认选中id,同步到状态中 + void _syncCheckState(List? checkIds) { + checkBoxStates.clear(); + checkIds?.forEach((element) { + checkBoxStates[element] = true; + }); + } + + + @override + void didUpdateWidget(TDCheckboxGroup oldWidget) { + super.didUpdateWidget(oldWidget); + final oldCheckIds = oldWidget.checkedIds; + final newCheckIds = widget.checkedIds; + if (oldCheckIds != newCheckIds) { + _syncCheckState(newCheckIds); + } + } + + + /// + /// 根据id获取CheckBox的勾选状态 + /// + /// + bool getCheckBoxStateById(String id, bool checked) { + if (checkBoxStates[id] == null) { + // checkBox本身的状态 + checkBoxStates[id] = checked; + } + return checkBoxStates[id]!; + } + + /// 勾选单个CheckBox + bool toggle(String id, bool check, [bool notify = false]) { + // 检查是否超过用户设置的最大可勾选数 + if (widget.maxChecked != null && check) { + if (checkBoxStates.count((k, v) => v) >= widget.maxChecked!) { + widget.onOverloadChecked?.call(); + return false; + } + } + checkBoxStates[id] = check; + if (notify) { + setState(() {}); + } + _notifyChange(); + return true; + } + + /// 操作所有CheckBox + void toggleAll(bool check, [bool notify = true]) { + var isChanged = false; + checkBoxStates.forEachCanBreak((k, v) { + if (check) { + if (!toggle(k, check)) { + // 勾选失败,退出循环 + return true; + } + } else { + toggle(k, check); + } + isChanged = true; + return false; + }); + + if (isChanged && notify) { + setState(() {}); + _notifyChange(); + } + } + + /// 反选 + void _reverseAll() { + final reverseValue = + checkBoxStates.map((key, value) => MapEntry(key, !value)); + checkBoxStates.forEach((key, value) { + checkBoxStates[key] = false; + }); + checkBoxStates.forEach((k, v) { + var check = reverseValue[k] ?? false; + toggle(k, check); + }); + setState(() {}); + _notifyChange(); + } + + void _notifyChange() { + final change = widget.onChangeGroup; + if (change != null) { + final checkedIds = checkBoxStates.where((k, v) => v).keys.toList(); + change.call(checkedIds); + } + } + + @override + Widget build(BuildContext context) { + return TDCheckboxGroupInherited(this, widget.child); + } +} + +class TDCheckboxGroupInherited extends InheritedWidget { + final TDCheckboxGroupState state; + + /// + /// 获取树上的Group节点 + /// + static TDCheckboxGroupInherited? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + const TDCheckboxGroupInherited(this.state, Widget child, {Key? key}) : super(child: child, key: key); + + @override + bool updateShouldNotify(covariant TDCheckboxGroupInherited oldWidget) { + return true; + } +} + + +class TDCheckboxGroupContainer extends TDCheckboxGroup { + + TDCheckboxGroupContainer({ + Key? key, + Widget? child, // 使用child 则请勿设置direction + Axis? direction, // direction 对 directionalTdRadios 起作用 + List? directionalTdCheckboxes, + List? selectIds, // 默认选择项的id组 + bool? passThrough, // 非通栏单选样式 用于使用child 或 direction == Axis.vertical 场景 + bool cardMode = false, + int? titleMaxLine, // item的行数 + int? maxSelected, // 最大勾选数 + TDCheckboxStyle? style,// 勾选样式 + TDCheckboxGroupController? controller, + IconBuilder? customIconBuilder, + ContentBuilder? customContentBuilder, + double? spacing, // icon和文字距离 + TDContentDirection? contentDirection, + OnCheckBoxGroupChange? onCheckBoxGroupChange, + VoidCallback? onOverloadChecked, + }) : assert(() { + // 使用direction属性则必须配合directionalTdCheckboxes,child字段无效 + if (direction != null && directionalTdCheckboxes == null) { + throw FlutterError( + '[TDCheckboxGroupContainer] direction and directionalTdCheckboxes must set at the same time'); + } + // 未使用direction则必须设置child + if (direction == null && child == null) { + throw FlutterError( + '[TDCheckboxGroupContainer] direction means use child as the exact one, but child is null'); + } + // 横向单选框 每个选项有字数限制 + if (direction == Axis.horizontal && directionalTdCheckboxes != null) { + directionalTdCheckboxes.forEach((element) { + if (element.subTitle != null) { + throw FlutterError( + 'horizontal checkbox style should not have subTilte, ' + 'because there left no room for it'); + } + }); + var maxWordCount = 2; + var tips = + '[TDCheckboxGroupContainer] checkbox title please not exceed $maxWordCount words.\n' + '2tabs: 7words maximum\n' + '3tabs: 4words maximum\n' + '4tabs: 2words maximum'; + if (directionalTdCheckboxes.length == 2) { + maxWordCount = 7; + } + if (directionalTdCheckboxes.length == 3) { + maxWordCount = 4; + } + if (directionalTdCheckboxes.length == 4) { + maxWordCount = 2; + } + directionalTdCheckboxes.forEach((checkbox) { + if ((checkbox.title?.length ?? 0) > maxWordCount) { + throw FlutterError(tips); + } + }); + } + // 卡片模式要求每个TDRadio必须设置cardMode属性为true,且不能有子标题(空间不够) + if (cardMode == true) { + assert(direction != null && directionalTdCheckboxes != null); + directionalTdCheckboxes!.forEach((element) { + // if use cardMode at TDRadioGroup, then every TDRadio should + // set it's own carMode to true. + if (element.cardMode == false) { + throw FlutterError( + 'if use cardMode at TDCheckboxGroupContainer, then every ' + 'TDCheckbox should set it\'s own carMode to true.'); + } + if (element.subTitle != null && direction == Axis.horizontal) { + throw FlutterError( + 'horizontal card style should not have subTilte, ' + 'because there left no room for it'); + } + }); + } + return true; + }()), + super( + child: Container( + clipBehavior: (passThrough ?? false) && direction != Axis.horizontal + ? Clip.hardEdge + : Clip.none, + decoration: (passThrough ?? false) && direction != Axis.horizontal + ? BoxDecoration(borderRadius: BorderRadius.circular(10)) + : null, + margin: (passThrough ?? false) && direction != Axis.horizontal + ? const EdgeInsets.symmetric(horizontal: 16) + : null, + child: direction == null + ? child! + : (direction == Axis.vertical + ? ListView.separated( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return Container( + margin: cardMode + ? const EdgeInsets.symmetric(horizontal: 16) + : null, + height: cardMode ? 82 : null, + child: directionalTdCheckboxes[index], + ); + }, + itemCount: directionalTdCheckboxes!.length, + separatorBuilder: (BuildContext context, int index) { + if (cardMode) { + return const SizedBox( + height: 12, + ); + } + return const SizedBox.shrink(); + }, + ) + : Container( + margin: cardMode + ? EdgeInsets.symmetric(horizontal: 16.scale) + : null, + alignment: cardMode ? Alignment.topLeft : null, + child: cardMode + ? Wrap( + spacing: 12.scale, + runSpacing: 12, + runAlignment: WrapAlignment.spaceEvenly, + children: directionalTdCheckboxes!.map((element) { + return SizedBox( + width: 106.3.scale, + height: 56, + child: element, + ); + }).toList(), + ) + : Row( + mainAxisSize: MainAxisSize.min, + children: directionalTdCheckboxes! + .map((e) => Expanded(child: e)) + .toList(), + ), + )), + ), + key: key, + onChangeGroup: (ids) { + selectIds = ids; + onCheckBoxGroupChange?.call(ids); + }, + onOverloadChecked: onOverloadChecked, + controller: controller, + checkedIds: selectIds, + maxChecked: maxSelected, + titleMaxLine: titleMaxLine, + contentDirection: contentDirection, + customIconBuilder: customIconBuilder, + customContentBuilder: customContentBuilder, + style: style, + spacing: spacing, + ); + + @override + State createState() { + return TDCheckboxGroupContainerState(); + } +} + +class TDCheckboxGroupContainerState extends TDCheckboxGroupState { + +} + +typedef OnCheckBoxGroupChange = void Function(List ids); \ No newline at end of file diff --git a/tdesign-component/lib/src/components/collapse/td_collapse.dart b/tdesign-component/lib/src/components/collapse/td_collapse.dart new file mode 100644 index 000000000..2a142c193 --- /dev/null +++ b/tdesign-component/lib/src/components/collapse/td_collapse.dart @@ -0,0 +1,347 @@ +/* + * Created by dorayhong@tencent.com on 6/4/23. + */ + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import 'td_collapse_panel.dart'; +import 'td_collapse_salted_key.dart'; +import 'td_inset_divider.dart'; +import 'td_nonanimated_expand_icon.dart'; + + +/// 折叠面板的组件样式 +enum TDCollapseStyle { + /// Block 通栏风格 + block, + + /// Card 卡片风格 + card +} + +/// 折叠面板列表组件,需配合 [TDCollapsePanel] 使用 +class TDCollapse extends StatefulWidget { + const TDCollapse({ + required this.children, + this.style = TDCollapseStyle.block, + this.expansionCallback, + this.animationDuration = kThemeAnimationDuration, + this.elevation = 0, + Key? key, + }) : _allowOnlyOnePanelOpen = false, + initialOpenPanelValue = null, + super(key: key); + + const TDCollapse.accordion({ + required this.children, + this.style = TDCollapseStyle.block, + this.expansionCallback, + this.animationDuration = kThemeAnimationDuration, + this.elevation = 0, + this.initialOpenPanelValue, + Key? key, + }) : _allowOnlyOnePanelOpen = true, + super(key: key); + + /// 折叠面板列表的样式 + /// - [TDCollapseStyle.block] 通栏风格 + /// - [TDCollapseStyle.card] 卡片风格 + final TDCollapseStyle style; + + /// 折叠面板列表的子组件 + final List children; + + /// 折叠面板列表的回调函数; + /// 回调时,入参为当前点击的折叠面板的索引 index 和是否展开的状态 isExpanded + final ExpansionPanelCallback? expansionCallback; + + /// 折叠面板列表的动画时长 + final Duration animationDuration; + + /// 折叠面板列表的阴影 + final double elevation; + + /// 折叠面板列表的默认展开面板的值; + /// 当使用 [TDCollapse.accordion] 时,此值生效 + final Object? initialOpenPanelValue; + + final bool _allowOnlyOnePanelOpen; + + @override + State createState() => _TDCollapseState(); +} + +class _TDCollapseState extends State { + TDCollapsePanel? _currentOpenPanel; + + @override + void initState() { + super.initState(); + + if (!widget._allowOnlyOnePanelOpen) { + return; + } + + assert(_allPanelsHaveValue(), + 'When allowing only one panel to be open, every panel must have a value.'); + assert(_allPanelsHaveDistinctValues(), + 'When allowing only one panel to be open, every panel must have a distinct value.'); + + if (widget.initialOpenPanelValue != null) { + _currentOpenPanel = _searchPanelByValue(widget.initialOpenPanelValue); + } + } + + @override + void didUpdateWidget(TDCollapse oldWidget) { + super.didUpdateWidget(oldWidget); + + if (!widget._allowOnlyOnePanelOpen) { + _currentOpenPanel = null; + return; + } + + assert(_allPanelsHaveValue(), + 'When allowing only one panel to be open, every panel must have a value.'); + assert(_allPanelsHaveDistinctValues(), + 'When allowing only one panel to be open, every panel must have a distinct value.'); + + // when the widget is updated to accordion mode + // we need to initialize the current open panel to defaultOpenPanelValue + if (!oldWidget._allowOnlyOnePanelOpen) { + _currentOpenPanel = _searchPanelByValue(widget.initialOpenPanelValue); + } + } + + @override + Widget build(BuildContext context) { + final items = []; + + for (var index = 0; index < widget.children.length; index += 1) { + if (_isChildExpanded(index) && + index != 0 && + !_isChildExpanded(index - 1)) { + items.add(_buildGap(context, index * 2 - 1)); + } + + final isLastChild = index == widget.children.length - 1; + final child = widget.children[index]; + + final titleWidget = _buildTitleWidget(context, child, index); + final expandIconWidget = _buildExpandIconWidget(context, child, index); + + final borderRadius = + _isCardStyle() ? _createRadius(index) : BorderRadius.zero; + + items.add( + MaterialSlice( + key: TDCollapseSaltedKey(context, index * 2), + color: child.backgroundColor, + child: Column( + // to prevent collapse state change when parent rebuild + key: TDCollapseSaltedKey(context, index * 2), + children: [ + MergeSemantics( + child: InkWell( + borderRadius: borderRadius, + onTap: () => _handlePressed(index, _isChildExpanded(index)), + child: Row( + children: [ + Expanded( + child: AnimatedContainer( + duration: widget.animationDuration, + curve: Curves.fastOutSlowIn, + margin: EdgeInsets.zero, + child: ConstrainedBox( + constraints: const BoxConstraints( + minHeight: kMinInteractiveDimension, + ), + child: titleWidget, + ), + ), + ), + expandIconWidget, + ], + ), + ), + ), + AnimatedCrossFade( + firstChild: Container(height: 0.0), + secondChild: Column( + children: [ + const TDInsetDivider(), + Container( + padding: EdgeInsets.all(TDTheme.of(context).spacer16), + child: child.body, + ), + ], + ), + firstCurve: + const Interval(0.0, 0.6, curve: Curves.fastOutSlowIn), + secondCurve: + const Interval(0.4, 1.0, curve: Curves.fastOutSlowIn), + sizeCurve: Curves.fastOutSlowIn, + crossFadeState: _isChildExpanded(index) + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + duration: widget.animationDuration, + ), + if (!isLastChild) const TDInsetDivider() + ], + )), + ); + + if (_isChildExpanded(index) && !isLastChild) { + items.add(_buildGap(context, index * 2 + 1)); + } + } + + // FIXME: 非连续展开的 item 会导致 expanded 时动画丢失 + Widget collapse = MergeableMaterial( + hasDividers: false, + elevation: widget.elevation, + children: items, + ); + + if (_isCardStyle()) { + collapse = Container( + child: ClipRRect( + child: collapse, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusLarge), + ), + margin: EdgeInsets.symmetric( + horizontal: TDTheme.of(context).spacer16, + ), + ); + } + + return collapse; + } + + MergeableMaterialItem _buildGap(BuildContext context, int value) { + return MaterialGap( + size: 0.0, + key: TDCollapseSaltedKey(context, value), + ); + } + + BorderRadius _createRadius(int index) { + final radius = Radius.circular(TDTheme.of(context).radiusLarge); + + final isFirst = index == 0; + if (isFirst) { + return BorderRadius.only(topLeft: radius, topRight: radius); + } + + final isLast = index == widget.children.length - 1; + if (isLast) { + return BorderRadius.only(bottomLeft: radius, bottomRight: radius); + } + + return BorderRadius.zero; + } + + bool _isCardStyle() { + return widget.style == TDCollapseStyle.card; + } + + bool _isChildExpanded(int index) { + final child = widget.children[index]; + + if (widget._allowOnlyOnePanelOpen) { + return _currentOpenPanel?.value == child.value; + } + + return child.isExpanded; + } + + void _handlePressed(int index, bool isExpanded) { + widget.expansionCallback?.call(index, isExpanded); + + if (!widget._allowOnlyOnePanelOpen) { + return; + } + + // collapse the current open panel by calling its expansion callback to false + for (var childIndex = 0; + childIndex < widget.children.length; + childIndex += 1) { + final curChild = widget.children[childIndex]; + if (widget.expansionCallback != null && + childIndex != index && + curChild.value == _currentOpenPanel?.value) { + widget.expansionCallback!(childIndex, false); + } + } + + setState(() { + _currentOpenPanel = isExpanded ? null : widget.children[index]; + }); + } + + Widget _buildTitleWidget( + BuildContext context, TDCollapsePanel child, int index) { + final titleWidget = child.headerBuilder(context, _isChildExpanded(index)); + return ListTile( + title: titleWidget, + ); + } + + Widget _buildExpandIconWidget( + BuildContext context, TDCollapsePanel child, int index) { + Widget expandedIcon = Container( + key: TDCollapseSaltedKey(context, index * 2), + margin: const EdgeInsetsDirectional.all(0.0), + child: TdNonAnimatedExpandIcon( + isExpanded: _isChildExpanded(index), + padding: child.expandIconTextBuilder != null + ? EdgeInsets.only( + right: TDTheme.of(context).spacer16, + top: TDTheme.of(context).spacer16, + bottom: TDTheme.of(context).spacer16, + left: 0, + ) + : EdgeInsets.all(TDTheme.of(context).spacer16), + ), + ); + + return Row( + children: [ + if (child.expandIconTextBuilder != null) + Text(child.expandIconTextBuilder!(context, _isChildExpanded(index)), + textAlign: TextAlign.right, + style: TextStyle( + color: Colors.black.withOpacity(0.4), + )), + expandedIcon, + ], + ); + } + + bool _allPanelsHaveValue() { + return widget.children.every((TDCollapsePanel child) { + return child.value != null; + }); + } + + bool _allPanelsHaveDistinctValues() { + final valueSet = {}; + return widget.children.every((TDCollapsePanel child) { + if (!valueSet.add(child.value)) { + return false; + } + return true; + }); + } + + TDCollapsePanel? _searchPanelByValue(Object? value) { + for (var index = 0; index < widget.children.length; index += 1) { + final child = widget.children[index]; + if (child.value == value) { + return child; + } + } + return null; + } +} diff --git a/tdesign-component/lib/src/components/collapse/td_collapse_panel.dart b/tdesign-component/lib/src/components/collapse/td_collapse_panel.dart new file mode 100644 index 000000000..a27499ab5 --- /dev/null +++ b/tdesign-component/lib/src/components/collapse/td_collapse_panel.dart @@ -0,0 +1,40 @@ +/* + * Created by dorayhong@tencent.com on 6/4/23. + */ +import 'package:flutter/material.dart'; + +typedef TDCollapseIconTextBuilder = String Function( + BuildContext context, bool isExpanded); + +/// 折叠面板,需配合 [TDCollapse] 使用 +class TDCollapsePanel extends ExpansionPanel { + TDCollapsePanel({ + /// 折叠面板的头部组件构造函数 + required ExpansionPanelHeaderBuilder headerBuilder, + + /// 折叠面板的内容组件 + required Widget body, + + /// 折叠面板的展开状态 + isExpanded = false, + + /// 折叠按钮操作说明文案的构造函数 + this.expandIconTextBuilder, + + /// 折叠面板的值,当使用 [TDCollapse.accordion] 时,必须传入此值 + this.value, + + /// 折叠面板的背景色 + backgroundColor, + }) : super( + headerBuilder: headerBuilder, + body: body, + isExpanded: isExpanded, + canTapOnHeader: true, + backgroundColor: backgroundColor, + ); + + final Object? value; + + final TDCollapseIconTextBuilder? expandIconTextBuilder; +} diff --git a/tdesign-component/lib/src/components/collapse/td_collapse_salted_key.dart b/tdesign-component/lib/src/components/collapse/td_collapse_salted_key.dart new file mode 100644 index 000000000..8e379c4d1 --- /dev/null +++ b/tdesign-component/lib/src/components/collapse/td_collapse_salted_key.dart @@ -0,0 +1,32 @@ +/* + * Created by dorayhong@tencent.com on 6/8/23. + */ +import 'package:flutter/cupertino.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../theme/td_theme.dart'; + +class TDCollapseSaltedKey extends LocalKey { + const TDCollapseSaltedKey(this.salt, this.value); + + final S salt; + final V value; + + @override + bool operator ==(dynamic other) { + if (other.runtimeType != runtimeType) return false; + final TDCollapseSaltedKey typedOther = other; + return salt == typedOther.salt && value == typedOther.value; + } + + @override + int get hashCode => Object.hash(runtimeType, salt, value); + + @override + String toString() { + + final saltString = S == String ? '<\'$salt\'>' : '<$salt>'; + final valueString = V == String ? '<\'$value\'>' : '<$value>'; + return '[$saltString $valueString]'; + } +} diff --git a/tdesign-component/lib/src/components/collapse/td_inset_divider.dart b/tdesign-component/lib/src/components/collapse/td_inset_divider.dart new file mode 100644 index 000000000..665cd049e --- /dev/null +++ b/tdesign-component/lib/src/components/collapse/td_inset_divider.dart @@ -0,0 +1,24 @@ +/* + * Created by dorayhong@tencent.com on 6/8/23. + */ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + + +class TDInsetDivider extends StatelessWidget { + const TDInsetDivider({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 1, + child: Divider( + color: TDTheme.of(context).grayColor3, + indent: TDTheme.of(context).spacer16, + endIndent: 0.0, + height: 1, + thickness: 0.5, + )); + } +} diff --git a/tdesign-component/lib/src/components/collapse/td_nonanimated_expand_icon.dart b/tdesign-component/lib/src/components/collapse/td_nonanimated_expand_icon.dart new file mode 100644 index 000000000..89880cfed --- /dev/null +++ b/tdesign-component/lib/src/components/collapse/td_nonanimated_expand_icon.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class TdNonAnimatedExpandIcon extends StatelessWidget { + const TdNonAnimatedExpandIcon({ + required this.isExpanded, + required this.padding, + Key? key, + }) : super(key: key); + + final bool isExpanded; + final EdgeInsets padding; + + Color getIconColor(BuildContext context) { + switch (Theme.of(context).brightness) { + case Brightness.light: + return Colors.black54; + case Brightness.dark: + return Colors.white60; + } + } + + @override + Widget build(BuildContext context) { + return IconButton( + padding: padding, + iconSize: 24.0, + color: getIconColor(context), + onPressed: null, + icon: isExpanded + ? const Icon(Icons.expand_less) + : const Icon(Icons.expand_more), + ); + } +} diff --git a/tdesign-component/lib/src/components/dialog/td_alert_dialog.dart b/tdesign-component/lib/src/components/dialog/td_alert_dialog.dart new file mode 100644 index 000000000..67300ec39 --- /dev/null +++ b/tdesign-component/lib/src/components/dialog/td_alert_dialog.dart @@ -0,0 +1,209 @@ +/* + * Created by haozhicao@tencent.com on 6/20/22. + * td_alert_dialog.dart + * + */ + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; +import 'td_dialog_widget.dart'; + +/// 弹窗控件 +/// +/// 支持横向或竖向摆放按钮 +/// 横向最多摆放两个按钮 +class TDAlertDialog extends StatelessWidget { + /// 横向按钮排列的对话框 + /// + /// [leftBtn]和[rightBtn]不传style参数会应用默认样式,左侧弱按钮,右侧强按钮 + const TDAlertDialog({ + Key? key, + this.backgroundColor = Colors.white, + this.radius = 12.0, + this.title, + this.titleColor = const Color(0xE6000000), + this.content, + this.contentColor, + this.titleAlignment, + this.contentWidget, + this.contentMaxHeight = 0, + this.leftBtn, + this.rightBtn, + this.leftBtnAction, + this.rightBtnAction, + this.showCloseButton, + TDDialogButtonStyle buttonStyle = TDDialogButtonStyle.normal, + this.padding = const EdgeInsets.fromLTRB(24, 32, 24, 0), + this.buttonWidget, + }) : assert((title != null || content != null || contentWidget != null)), + _vertical = false, + _buttons = null, + _buttonStyle = buttonStyle, + super(key: key); + + /// 纵向按钮排列的对话框 + /// + /// [buttons]参数是必须的,纵向按钮默认样式都是[TDButtonTheme.primary] + const TDAlertDialog.vertical({ + Key? key, + required List buttons, + this.backgroundColor = Colors.white, + this.radius = 12.0, + this.title, + this.titleColor = Colors.black, + this.titleAlignment, + this.contentWidget, + this.content, + this.contentColor, + this.contentMaxHeight = 0, + this.showCloseButton, + this.padding = const EdgeInsets.fromLTRB(24, 32, 24, 0), + this.buttonWidget, + }) : _vertical = true, + leftBtn = null, + rightBtn = null, + _buttons = buttons, + _buttonStyle = TDDialogButtonStyle.normal, + leftBtnAction = null, + rightBtnAction = null, + super(key: key); + + /// 背景颜色 + final Color backgroundColor; + + /// 圆角 + final double radius; + + /// 标题 + final String? title; + + /// 标题颜色 + final Color titleColor; + + /// 标题对齐模式 + final AlignmentGeometry? titleAlignment; + + /// 内容Widget + final Widget? contentWidget; + + /// 内容 + final String? content; + + /// 内容颜色 + final Color? contentColor; + + /// 内容的最大高度,默认为0,也就是不限制高度 + final double contentMaxHeight; + + /// 左侧按钮配置 + final TDDialogButtonOptions? leftBtn; + + /// 右侧按钮配置 + final TDDialogButtonOptions? rightBtn; + + /// 左侧按钮默认点击 + final Function()? leftBtnAction; + + /// 右侧按钮默认点击 + final Function()? rightBtnAction; + + /// 显示右上角关闭按钮 + final bool? showCloseButton; + + /// 选项是否是垂直排布,默认是左右排布 + final bool _vertical; + + /// 垂直排布的按钮列表 + final List? _buttons; + + /// 按钮样式 + /// + /// 支持普通类型和文字类型按钮 + /// 文字类型仅支持横向排列 + /// [leftBtn]和[rightBtn]中的style会覆盖此配置 + final TDDialogButtonStyle _buttonStyle; + + /// 内容内边距 + final EdgeInsets? padding; + + /// 自定义按钮 + final Widget? buttonWidget; + + @override + Widget build(BuildContext context) { + // 标题和内容不能同时为空 + return TDDialogScaffold( + showCloseButton: showCloseButton, + backgroundColor: backgroundColor, + radius: radius, + body: Column(mainAxisSize: MainAxisSize.min, children: [ + TDDialogInfoWidget( + title: title, + titleColor: titleColor, + titleAlignment: titleAlignment, + contentWidget: contentWidget, + content: content, + contentColor: contentColor, + contentMaxHeight: contentMaxHeight, + padding: padding, + ), + const TDDivider(height: 24, color: Colors.transparent), + _vertical ? _verticalButtons(context) : _horizontalButtons(context), + ])); + } + + Widget _horizontalButtons(BuildContext context) { + if(buttonWidget != null) { + return buttonWidget!; + } + final left = leftBtn ?? + TDDialogButtonOptions( + title: context.resource.cancel, theme: TDButtonTheme.light, action: leftBtnAction); + final right = rightBtn ?? + TDDialogButtonOptions( + title: context.resource.confirm, theme: TDButtonTheme.primary, action: rightBtnAction); + return _buttonStyle == TDDialogButtonStyle.text + ? HorizontalTextButtons(leftBtn: left, rightBtn: right) + : HorizontalNormalButtons( + leftBtn: left, + rightBtn: right, + ); + } + + Widget _verticalButtons(BuildContext context) { + var widgets = []; + _buttons!.asMap().forEach((index, value) { + Widget btn = TDDialogButton( + buttonText: value.title, + buttonTextColor: value.titleColor, + buttonTextSize: value.titleSize, + height: value.height, + buttonTextFontWeight: value.fontWeight ?? FontWeight.w600, + buttonStyle: value.style, + buttonTheme: value.theme, + buttonType: value.type, + onPressed: () { + if(value.action != null){ + value.action!(); + } else { + Navigator.pop(context); + } + }, + ); + widgets.add(btn); + if (index < _buttons!.length - 1) { + widgets.add(const TDDivider(height: 12, color: Colors.transparent)); + } + }); + + return Container( + padding: + const EdgeInsets.only(left: 24, right: 24, bottom: 24), + child: Column( + children: widgets, + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/dialog/td_confirm_dialog.dart b/tdesign-component/lib/src/components/dialog/td_confirm_dialog.dart new file mode 100644 index 000000000..46a6861b0 --- /dev/null +++ b/tdesign-component/lib/src/components/dialog/td_confirm_dialog.dart @@ -0,0 +1,171 @@ +/* + * Created by haozhicao@tencent.com on 6/20/22. + * td_confirm_dialog.dart + * + */ + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; +import 'td_dialog_widget.dart'; + +/// 只有一个按钮的弹窗控件 +/// +/// 按钮样式支持普通和文字 +class TDConfirmDialog extends StatelessWidget { + const TDConfirmDialog({ + Key? key, + this.action, + this.backgroundColor = Colors.white, + this.radius = 12.0, + this.title, + this.titleColor = const Color(0xE6000000), + this.titleAlignment, + this.contentWidget, + this.content, + this.contentColor, + this.contentMaxHeight = 0, + this.buttonText, + this.buttonTextColor, + this.buttonStyle = TDDialogButtonStyle.normal, + this.showCloseButton, + this.padding = const EdgeInsets.fromLTRB(24, 32, 24, 0), + this.buttonWidget, + }) : super(key: key); + + /// 标题 + final String? title; + + /// 标题颜色 + final Color titleColor; + + /// 标题对齐模式 + final AlignmentGeometry? titleAlignment; + + /// 内容Widget + final Widget? contentWidget; + + /// 内容 + final String? content; + + /// 内容颜色 + final Color? contentColor; + + /// 内容的最大高度,默认为0,也就是不限制高度 + final double contentMaxHeight; + + /// 按钮文字 + final String? buttonText; + + /// 按钮文字颜色 + final Color? buttonTextColor; + + /// 点击 + final Function()? action; + + /// 背景颜色 + final Color backgroundColor; + + /// 按钮样式 + final TDDialogButtonStyle buttonStyle; + + /// 圆角 + final double radius; + + /// 右上角关闭按钮 + final bool? showCloseButton; + + /// 内容内边距 + final EdgeInsets? padding; + + /// 自定义按钮 + final Widget? buttonWidget; + + Widget _buildButton(BuildContext context) { + if (buttonWidget != null) { + return buttonWidget!; + } + if (buttonStyle == TDDialogButtonStyle.text) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const TDDivider(height: 23, color: Colors.transparent), + const TDDivider(height: 1), + TDDialogButton( + buttonText: buttonText ?? context.resource.knew, + buttonTextColor: buttonTextColor, + buttonType: TDButtonType.text, + buttonTheme: TDButtonTheme.primary, + height: 56, + onPressed: () { + if (action != null) { + action!(); + } else { + Navigator.pop(context); + } + }, + ) + ], + ); + } else { + return Container( + padding: const EdgeInsets.fromLTRB(24, 24, 24, 24), + child: TDDialogButton( + buttonText: buttonText ?? context.resource.knew, + buttonTextColor: buttonTextColor, + buttonTheme: TDButtonTheme.primary, + onPressed: () { + if (action != null) { + action!(); + } else { + Navigator.pop(context); + } + }, + ), + ); + } + } + + @override + Widget build(BuildContext context) { + // 标题和内容不能同时为空 + assert((title != null || content != null || contentWidget != null)); + + return TDDialogScaffold( + showCloseButton: showCloseButton, + backgroundColor: backgroundColor, + radius: radius, + body: LayoutBuilder( + builder: (context, constraints) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 内容区域添加弹性约束 https://api.flutter.dev/flutter/widgets/Flexible-class.html + Flexible( + // 滚动支持 + child: SingleChildScrollView( + physics: const ClampingScrollPhysics(), + child: TDDialogInfoWidget( + title: title, + titleColor: titleColor, + titleAlignment: titleAlignment, + contentWidget: contentWidget, + content: content, + contentColor: contentColor, + // 当contentMaxHeight未设置时,使用屏幕的60%作为最大高度,并允许滚动 + contentMaxHeight: contentMaxHeight > 0 + ? contentMaxHeight + : constraints.maxHeight * 0.6, + padding: padding, + ), + ), + ), + _buildButton(context), + ] + ); + } + ) + ); + } +} diff --git a/tdesign-component/lib/src/components/dialog/td_dialog.dart b/tdesign-component/lib/src/components/dialog/td_dialog.dart new file mode 100644 index 000000000..ae83e03a1 --- /dev/null +++ b/tdesign-component/lib/src/components/dialog/td_dialog.dart @@ -0,0 +1,67 @@ +/* + * Created by haozhicao@tencent.com on 6/17/22. + * td_dialog.dart + * + */ + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +export 'td_alert_dialog.dart'; +export 'td_confirm_dialog.dart'; +export 'td_image_dialog.dart'; +export 'td_input_dialog.dart'; + +/// Dialog按钮样式 +/// +/// 用于在Dialog层面配置按钮样式 +/// Dialog内支持配置每个按钮的样式 +enum TDDialogButtonStyle { + normal, + text, +} + +/// 弹窗按钮配置 +class TDDialogButtonOptions { + TDDialogButtonOptions({ + required this.title, + required this.action, + this.titleColor, + this.titleSize, + this.style, + this.type, + this.theme, + this.height, + this.fontWeight, + }); + + /// 标题内容 + final String title; + + /// 标题颜色 + Color? titleColor; + + /// 字体大小 + final double? titleSize; + + /// 字体粗细 + final FontWeight? fontWeight; + + /// 按钮样式 + /// 设置单个按钮的样式会覆盖Dialog的默认样式 + final TDButtonStyle? style; + + /// 按钮类型 + final TDButtonType? type; + + /// 按钮类型 + final TDButtonTheme? theme; + + /// 按钮高度 + /// 建议使用默认高度 + final double? height; + + /// 点击操作 + final Function()? action; +} diff --git a/tdesign-component/lib/src/components/dialog/td_dialog_widget.dart b/tdesign-component/lib/src/components/dialog/td_dialog_widget.dart new file mode 100644 index 000000000..9948d9e90 --- /dev/null +++ b/tdesign-component/lib/src/components/dialog/td_dialog_widget.dart @@ -0,0 +1,420 @@ +/* + * Created by haozhicao@tencent.com on 6/20/22. + * td_dialog_widget.dart + * + */ + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +/// TDDialog手脚架 +class TDDialogScaffold extends StatelessWidget { + const TDDialogScaffold({ + Key? key, + required this.body, + this.showCloseButton, + this.backgroundColor = Colors.white, + this.radius = 12.0, + }) : super(key: key); + + /// Dialog主体 + final Widget body; + + /// 显示右上角关闭按钮 + final bool? showCloseButton; + + /// 背景色 + final Color backgroundColor; + + /// 圆角 + final double radius; + + @override + Widget build(BuildContext context) { + return Center( + child: Material( + type: MaterialType.transparency, + child: Container( + width: 311, + decoration: BoxDecoration( + color: backgroundColor, // 底色 + borderRadius: BorderRadius.all(Radius.circular(radius)), + ), + child: Stack( + children: [ + body, + showCloseButton ?? false + ? Positioned( + top: 0, + right: 0, + child: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: SizedBox( + width: 38, + height: 38, + child: Center( + child: Icon( + TDIcons.close, + size: 22, + color: TDTheme.of(context).fontGyColor3, + ), + ), + ), + )) + : Container(height: 0) + ], + ), + ), + ), + ); + } +} + +/// 弹窗标题 +class TDDialogTitle extends StatelessWidget { + const TDDialogTitle({ + Key? key, + this.title, + this.titleColor = Colors.black, + }) : super(key: key); + + /// 标题颜色 + final Color titleColor; + + /// 标题文字 + final String? title; + + @override + Widget build(BuildContext context) { + // 标题和内容不能同时为空 + return TDText( + title, + textColor: titleColor, + fontWeight: FontWeight.w600, + font: Font(size: 16, lineHeight: 24), + textAlign: TextAlign.center, + ); + } +} + +/// 弹窗内容 +class TDDialogContent extends StatelessWidget { + const TDDialogContent({ + Key? key, + this.content, + this.contentColor = const Color(0x99000000), + }) : super(key: key); + + /// 标题颜色 + final Color contentColor; + + /// 标题文字 + final String? content; + + @override + Widget build(BuildContext context) { + // 标题和内容不能同时为空 + return TDText( + content, + textColor: contentColor, + font: Font(size: 16, lineHeight: 24), + textAlign: TextAlign.center, + ); + } +} + +/// 弹窗信息 +class TDDialogInfoWidget extends StatelessWidget { + const TDDialogInfoWidget({ + Key? key, + this.title, + this.titleColor = Colors.black, + this.titleAlignment, + this.contentWidget, + this.content, + this.contentColor, + this.contentMaxHeight = 0, + this.padding = const EdgeInsets.fromLTRB(24, 32, 24, 0), + }) : super(key: key); + + /// 标题 + final String? title; + + /// 标题颜色 + final Color titleColor; + + /// 标题对齐模式 + final AlignmentGeometry? titleAlignment; + + /// 内容Widget + final Widget? contentWidget; + + /// 内容 + final String? content; + + /// 内容颜色 + final Color? contentColor; + + /// 内容的最大高度,默认为0,也就是不限制高度 + final double contentMaxHeight; + + /// 内容的内边距 + final EdgeInsetsGeometry? padding; + + @override + Widget build(BuildContext context) { + // 标题和内容不能同时为空 + assert((title != null || content != null || contentWidget != null)); + return Container( + padding: padding, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (title != null) + Align( + alignment: titleAlignment ?? Alignment.center, + child: TDText( + title, + textColor: titleColor, + fontWeight: FontWeight.w600, + font: Font(size: 18, lineHeight: 26), + textAlign: TextAlign.center, + ), + ), + if (contentWidget != null || content != null) + Container( + padding: EdgeInsets.fromLTRB(0, (title != null && content != null) ? 8.0 : 0, 0, 0), + constraints: contentMaxHeight > 0 + ? BoxConstraints( + maxHeight: contentMaxHeight, + ) + : null, + child: contentWidget ?? + Scrollbar( + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: TDDialogContent( + content: content!, + contentColor: contentColor ?? const Color(0x99000000), + ), + ), + ), + ), + ], + ), + ); + } +} + +/// 横向排列的两个按钮 +class HorizontalNormalButtons extends StatelessWidget { + const HorizontalNormalButtons({ + Key? key, + required this.leftBtn, + required this.rightBtn, + }) : super(key: key); + + /// 左按钮 + final TDDialogButtonOptions leftBtn; + + /// 右按钮 + final TDDialogButtonOptions rightBtn; + + @override + Widget build(BuildContext context) { + // 标题和内容不能同时为空 + return Container( + padding: const EdgeInsets.fromLTRB(24, 0, 24, 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: TDDialogButton( + buttonText: leftBtn.title, + buttonTextColor: leftBtn.titleColor, + buttonTextSize: leftBtn.titleSize, + buttonStyle: leftBtn.style, + buttonType: leftBtn.type, + buttonTheme: leftBtn.theme, + height: leftBtn.height, + buttonTextFontWeight: leftBtn.fontWeight ?? FontWeight.w600, + onPressed: () { + if(leftBtn.action != null){ + leftBtn.action!(); + } else { + Navigator.pop(context); + } + }, + ), + ), + const TDDivider( + width: 12, + color: Colors.transparent, + ), + Expanded( + child: TDDialogButton( + buttonText: rightBtn.title, + buttonTextColor: rightBtn.titleColor, + buttonTextSize: rightBtn.titleSize, + buttonStyle: rightBtn.style, + buttonType: rightBtn.type, + buttonTheme: rightBtn.theme, + height: rightBtn.height, + buttonTextFontWeight: rightBtn.fontWeight ?? FontWeight.w600, + onPressed: () { + if(rightBtn.action != null) { + rightBtn.action!(); + } else { + Navigator.pop(context); + } + }, + ), + ), + ], + ), + ); + } +} + +/// 左右横向文字按钮,顶部和中间有分割线 +class HorizontalTextButtons extends StatelessWidget { + const HorizontalTextButtons({ + Key? key, + required this.leftBtn, + required this.rightBtn, + }) : super(key: key); + + /// 左按钮 + final TDDialogButtonOptions leftBtn; + + /// 右按钮 + final TDDialogButtonOptions rightBtn; + + @override + Widget build(BuildContext context) { + // 标题和内容不能同时为空 + return Column( + children: [ + const TDDivider(height: 1), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: TDDialogButton( + buttonText: leftBtn.title, + buttonTextColor: leftBtn.titleColor, + buttonTextSize: leftBtn.titleSize, + buttonStyle: leftBtn.style, + buttonType: leftBtn.type ?? TDButtonType.text, + buttonTheme: leftBtn.theme, + // fix: The button height does not fill the container. + height: 56, + buttonTextFontWeight: leftBtn.fontWeight, + onPressed: () { + if(leftBtn.action != null){ + leftBtn.action!(); + } else { + Navigator.pop(context); + } + }, + ), + ), + const TDDivider( + width: 1, + height: 56, + ), + Expanded( + child: TDDialogButton( + buttonText: rightBtn.title, + buttonTextColor: rightBtn.titleColor, + buttonTextSize: rightBtn.titleSize, + buttonStyle: rightBtn.style, + buttonType: rightBtn.type ?? TDButtonType.text, + buttonTheme: rightBtn.theme ?? TDButtonTheme.primary, + height: 56, + buttonTextFontWeight: rightBtn.fontWeight ?? FontWeight.w600, + onPressed: () { + if(rightBtn.action != null){ + rightBtn.action!(); + } else { + Navigator.pop(context); + } + }, + ), + ), + ], + ) + ], + ); + } +} + +/// 弹窗标题 +class TDDialogButton extends StatelessWidget { + const TDDialogButton({ + Key? key, + this.buttonText, + this.buttonTextColor, + this.buttonTextSize, + this.buttonTextFontWeight = FontWeight.w600, + this.buttonStyle, + this.buttonType, + this.buttonTheme, + required this.onPressed, + this.height = 40.0, + this.width, + this.isBlock = true, + }) : super(key: key); + + /// 按钮文字 + final String? buttonText; + + /// 按钮文字颜色 + final Color? buttonTextColor; + + /// 按钮文字大小 + final double? buttonTextSize; + + /// 按钮文字粗细 + final FontWeight? buttonTextFontWeight; + + /// 按钮样式 + final TDButtonStyle? buttonStyle; + + /// 按钮类型 + final TDButtonType? buttonType; + + /// 按钮主题 + final TDButtonTheme? buttonTheme; + + /// 按钮宽度 + final double? width; + + /// 按钮高度 + final double? height; + + /// 按钮高度 + final bool isBlock; + + /// 点击 + final Function() onPressed; + + @override + Widget build(BuildContext context) { + return TDButton( + onTap: onPressed, + style: buttonStyle, + type: buttonType ?? TDButtonType.fill, + theme: buttonTheme, + text: buttonText, + textStyle: TextStyle(fontWeight: buttonTextFontWeight,color:buttonTextColor,fontSize: buttonTextSize), + width: width, + height: height, + isBlock: isBlock, + margin: EdgeInsets.zero, + ); + } +} diff --git a/tdesign-component/lib/src/components/dialog/td_image_dialog.dart b/tdesign-component/lib/src/components/dialog/td_image_dialog.dart new file mode 100644 index 000000000..e1e0c4ba3 --- /dev/null +++ b/tdesign-component/lib/src/components/dialog/td_image_dialog.dart @@ -0,0 +1,186 @@ +/* + * Created by haozhicao@tencent.com on 6/20/22. + * td_image_dialog.dart + * + */ + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; +import 'td_dialog_widget.dart'; + +enum TDDialogImagePosition { + top, + middle, +} + +/// 带有图片的弹窗控件 +class TDImageDialog extends StatelessWidget { + const TDImageDialog({ + Key? key, + required this.image, + this.imagePosition = TDDialogImagePosition.top, + this.backgroundColor = Colors.white, + this.radius = 12.0, + this.title, + this.titleColor = const Color(0xE6000000), + this.titleAlignment, + this.contentWidget, + this.content, + this.contentColor, + this.leftBtn, + this.rightBtn, + this.showCloseButton, + this.padding, + this.buttonWidget, + }) : super(key: key); + + /// 背景颜色 + final Color backgroundColor; + + /// 圆角 + final double radius; + + /// 标题 + final String? title; + + /// 标题颜色 + final Color titleColor; + + /// 标题对齐模式 + final AlignmentGeometry? titleAlignment; + + /// 内容Widget + final Widget? contentWidget; + + /// 内容 + final String? content; + + /// 内容颜色 + final Color? contentColor; + + /// 左侧按钮配置 + final TDDialogButtonOptions? leftBtn; + + /// 右侧按钮配置 + final TDDialogButtonOptions? rightBtn; + + /// 图片 + final Image image; + + /// 图片位置 + final TDDialogImagePosition? imagePosition; + + /// 显示右上角关闭按钮 + final bool? showCloseButton; + + /// 内容内边距 + final EdgeInsets? padding; + + /// 自定义按钮 + final Widget? buttonWidget; + + Widget _buildImage(BuildContext context) { + return SizedBox( + width: 311, + height: 160, + child: FittedBox( + fit: BoxFit.cover, + child: image, + ), + ); + } + + Widget _buildTopImage(BuildContext context) { + return Column(mainAxisSize: MainAxisSize.min, children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(radius), + topRight: Radius.circular(radius)), + child: _buildImage(context), + ), + TDDialogInfoWidget( + title: title, + padding: padding ?? const EdgeInsets.fromLTRB(24, 24, 24, 0), + titleColor: titleColor, + titleAlignment: titleAlignment, + contentWidget: contentWidget, + content: content, + contentColor: contentColor, + ), + const TDDivider(height: 24, color: Colors.transparent), + _horizontalButtons(context), + ]); + } + + Widget _buildMiddleImage(BuildContext context) { + return Column(mainAxisSize: MainAxisSize.min, children: [ + TDDialogInfoWidget( + padding: padding ?? const EdgeInsets.fromLTRB(24, 24, 24, 0), + title: title, + titleColor: titleColor, + titleAlignment: titleAlignment, + contentWidget: contentWidget, + content: content, + contentColor: contentColor, + ), + Container( + padding: const EdgeInsets.only(top: 24), + child: ClipRRect( + child: _buildImage(context), + ), + ), + const TDDivider(height: 24, color: Colors.transparent), + _horizontalButtons(context), + ]); + } + + Widget _buildOnlyImage(BuildContext context) { + return Column(mainAxisSize: MainAxisSize.min, children: [ + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(radius), + topRight: Radius.circular(radius)), + child: _buildImage(context), + ), + const TDDivider(height: 24, color: Colors.transparent), + _horizontalButtons(context), + ]); + } + + Widget _buildBody(BuildContext context) { + if (title == null && content == null) { + return _buildOnlyImage(context); + } else if (imagePosition == TDDialogImagePosition.middle) { + return _buildMiddleImage(context); + } else { + return _buildTopImage(context); + } + } + + @override + Widget build(BuildContext context) { + return TDDialogScaffold( + showCloseButton: showCloseButton, + backgroundColor: backgroundColor, + radius: radius, + body: _buildBody(context)); + } + + Widget _horizontalButtons(BuildContext context) { + if (buttonWidget != null) { + return buttonWidget!; + } + final left = leftBtn ?? + TDDialogButtonOptions( + title: context.resource.cancel, theme: TDButtonTheme.light, action: null); + final right = rightBtn ?? + TDDialogButtonOptions( + title: context.resource.confirm, theme: TDButtonTheme.primary, action: null); + return HorizontalNormalButtons( + leftBtn: left, + rightBtn: right, + ); + } +} diff --git a/tdesign-component/lib/src/components/dialog/td_input_dialog.dart b/tdesign-component/lib/src/components/dialog/td_input_dialog.dart new file mode 100644 index 000000000..ef6a1063c --- /dev/null +++ b/tdesign-component/lib/src/components/dialog/td_input_dialog.dart @@ -0,0 +1,152 @@ +/* + * Created by haozhicao@tencent.com on 6/20/22. + * td_input_dialog.dart + * + */ + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; +import 'td_dialog_widget.dart'; + +/// 带有输入框的弹窗 +class TDInputDialog extends StatelessWidget { + const TDInputDialog({ + Key? key, + required this.textEditingController, + this.backgroundColor = Colors.white, + this.radius = 12.0, + this.title, + this.titleColor = const Color(0xE6000000), + this.titleAlignment, + this.contentWidget, + this.content, + this.hintText = '', + this.contentColor, + this.leftBtn, + this.rightBtn, + this.showCloseButton, + this.padding = const EdgeInsets.fromLTRB(24, 32, 24, 0), + this.buttonWidget, + this.customInputWidget, + }) : assert((title != null || content != null || contentWidget != null)), + super(key: key); + + /// 背景颜色 + final Color backgroundColor; + + /// 圆角 + final double radius; + + /// 标题 + final String? title; + + /// 标题颜色 + final Color titleColor; + + /// 标题对齐模式 + final AlignmentGeometry? titleAlignment; + + /// 内容Widget + final Widget? contentWidget; + + /// 内容 + final String? content; + + /// 输入提示 + final String? hintText; + + /// 内容颜色 + final Color? contentColor; + + /// 输入controller + final TextEditingController textEditingController; + + /// 左侧按钮配置 + final TDDialogButtonOptions? leftBtn; + + /// 右侧按钮配置 + final TDDialogButtonOptions? rightBtn; + + /// 显示右上角关闭按钮 + final bool? showCloseButton; + + /// 内容内边距 + final EdgeInsets? padding; + + /// 自定义按钮 + final Widget? buttonWidget; + + /// 自定义输入框 + final Widget? customInputWidget; + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: TDDialogScaffold( + showCloseButton: showCloseButton, + backgroundColor: backgroundColor, + radius: radius, + body: Material( + color: backgroundColor, + borderRadius: BorderRadius.all(Radius.circular(radius)), + child: Column(mainAxisSize: MainAxisSize.min, children: [ + TDDialogInfoWidget( + title: title, + titleColor: titleColor, + titleAlignment: titleAlignment, + contentWidget: contentWidget, + content: content, + contentColor: contentColor, + padding: padding, + ), + SizedBox( + child: customInputWidget != null + ? customInputWidget! + : Container( + color: Colors.white, + height: 48, + margin: const EdgeInsets.fromLTRB(24, 16, 24, 24), + child: TextField( + controller: textEditingController, + autofocus: true, + decoration: InputDecoration( + contentPadding: const EdgeInsets.fromLTRB(16, 0, 16, 0), + border: OutlineInputBorder(borderRadius: BorderRadius.circular(6), borderSide: BorderSide.none), + hintText: hintText, + hintStyle: const TextStyle(color: Color(0x66000000)), + fillColor: const Color(0xFFF3F3F3), + filled: true, + // labelText: '左上角', + ), + ), + ), + ), + _horizontalButtons(context), + ]), + ), + ), + ); + } + + Widget _horizontalButtons(BuildContext context) { + if (buttonWidget != null) { + return buttonWidget!; + } + final left = leftBtn ?? + TDDialogButtonOptions( + title: context.resource.cancel, + titleColor: const Color(0xE6000000), + fontWeight: FontWeight.normal, + action: null, + height: 56); + final right = rightBtn ?? + TDDialogButtonOptions(title: context.resource.confirm, action: null, fontWeight: FontWeight.w600, height: 56); + return HorizontalTextButtons( + leftBtn: left, + rightBtn: right, + ); + } +} diff --git a/lib/src/components/divider/dashed_widget.dart b/tdesign-component/lib/src/components/divider/dashed_widget.dart similarity index 100% rename from lib/src/components/divider/dashed_widget.dart rename to tdesign-component/lib/src/components/divider/dashed_widget.dart diff --git a/tdesign-component/lib/src/components/divider/td_divider.dart b/tdesign-component/lib/src/components/divider/td_divider.dart new file mode 100644 index 000000000..60f48dc1e --- /dev/null +++ b/tdesign-component/lib/src/components/divider/td_divider.dart @@ -0,0 +1,214 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import 'dashed_widget.dart'; + +enum TextAlignment { + left, + center, + right +} + +/// 分割线 +/// 对于非flutter原有的控件,则只需满足TDesign规范即可; +/// 如果有业务在实际使用,还需兼容实际业务场景。 +class TDDivider extends StatelessWidget { + const TDDivider({ + Key? key, + this.color, + this.margin, + this.width, + this.height, + this.text, + this.textStyle, + this.widget, + this.gapPadding, + this.hideLine = false, + this.isDashed = false, + this.alignment = TextAlignment.center, + this.direction = Axis.horizontal, + }) : super(key: key); + + /// 线条颜色 + final Color? color; + + /// 文字位置 + final TextAlignment alignment; + + /// 外部填充 + final EdgeInsetsGeometry? margin; + + /// 线条和中间文本之间的填充 + final EdgeInsetsGeometry? gapPadding; + + /// 宽度,需要竖向线条时使用 + final double? width; + + /// 高度,横向线条使用 + final double? height; + + /// 文本字符串,使用默认样式 + final String? text; + + /// 自定义文本样式 + final TextStyle? textStyle; + + /// 中间控件,可自定义样式 + final Widget? widget; + + /// 隐藏线条,使用纯文本分割 + final bool hideLine; + + /// 是否为虚线 + final bool isDashed; + + /// 方向,竖直虚线必须传 + final Axis direction; + + @override + Widget build(BuildContext context) { + // 普通直线 + if (widget == null && text == null) { + return _buildLine(context, + width: width, height: height, margin: margin, color: color); + } + + // 隐藏线条,纯文本分割 + if (hideLine) { + return Container( + width: width, + height: height, + margin: margin, + child: _buildMiddleWidget(context), + ); + } + + // 文本+线条 + return _buildDivider(context, alignment); + } + + Widget _buildDivider(BuildContext context, TextAlignment alignment) { + switch(alignment) { + case TextAlignment.left: + return Container( + width: width, + margin: margin, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _buildLine( + context, + width: 16, + height: height ?? 0.5, + color: color ?? TDTheme.of(context).grayColor3, + ), + Padding( + padding: gapPadding ?? const EdgeInsets.only(left: 8, right: 8), + child: _buildMiddleWidget(context)), + Expanded( + child: Center( + child: _buildLine( + context, + height: height ?? 0.5, + color: color ?? TDTheme.of(context).grayColor3, + ))), + ], + ), + ); + case TextAlignment.center: + return Container( + width: width, + margin: margin, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Center( + child: _buildLine( + context, + height: height ?? 0.5, + color: color ?? TDTheme.of(context).grayColor3, + ), + )), + Padding( + padding: gapPadding ?? const EdgeInsets.only(left: 8, right: 8), + child: _buildMiddleWidget(context)), + Expanded( + child: Center( + child: _buildLine( + context, + height: height ?? 0.5, + color: color ?? TDTheme.of(context).grayColor3, + ))), + ], + ), + ); + case TextAlignment.right: + return Container( + width: width, + margin: margin, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Center( + child: _buildLine( + context, + height: height ?? 0.5, + color: color ?? TDTheme.of(context).grayColor3, + ), + )), + Padding( + padding: gapPadding ?? const EdgeInsets.only(left: 8, right: 8), + child: _buildMiddleWidget(context)), + _buildLine( + context, + width: 16, + height: height ?? 0.5, + color: color ?? TDTheme.of(context).grayColor3, + ), + ], + ), + ); + } + } + + /// 绘制线条 + Container _buildLine(BuildContext context, + {double? width, + double? height, + EdgeInsetsGeometry? margin, + Color? color}) { + if (isDashed) { + return Container( + width: width, + margin: margin, + child: DashedWidget( + width: width, + height: height, + color: color ?? TDTheme.of(context).grayColor3, + direction: direction, + ), + ); + } else { + return Container( + width: width, + height: height ?? 0.5, + margin: margin, + color: color ?? TDTheme.of(context).grayColor3, + ); + } + } + + /// 构建中间控件 + Widget _buildMiddleWidget(BuildContext context) { + return widget ?? + TDText( + text, + font: TDTheme.of(context).fontBodySmall, + textColor: TDTheme.of(context).fontGyColor3, + forceVerticalCenter: true, + style: textStyle, + ); + } +} diff --git a/tdesign-component/lib/src/components/drawer/td_drawer.dart b/tdesign-component/lib/src/components/drawer/td_drawer.dart new file mode 100644 index 000000000..14564c7a4 --- /dev/null +++ b/tdesign-component/lib/src/components/drawer/td_drawer.dart @@ -0,0 +1,151 @@ +import 'package:flutter/material.dart'; + +import '../../theme/td_colors.dart'; +import '../../theme/td_spacers.dart'; +import '../../theme/td_theme.dart'; +import '../cell/td_cell.dart'; +import '../cell/td_cell_group.dart'; +import '../cell/td_cell_style.dart'; +import '../icon/td_icons.dart'; +import '../popup/td_popup_route.dart'; +import 'td_drawer_widget.dart'; + +/// 抽屉方向 +enum TDDrawerPlacement { left, right } + +/// 抽屉组件 +class TDDrawer { + TDDrawer( + this.context, { + this.closeOnOverlayClick = true, + this.footer, + this.items, + this.placement = TDDrawerPlacement.right, + this.showOverlay = true, + this.title, + this.titleWidget, + this.visible, + this.onClose, + this.onItemClick, + this.width = 280, + this.drawerTop, + this.style, + this.hover = true, + this.backgroundColor, + this.bordered = true, + this.isShowLastBordered = true, + this.contentWidget, + }) { + if (visible == true) { + show(); + } + } + + /// 上下文 + final BuildContext context; + + /// 点击蒙层时是否关闭抽屉 + final bool? closeOnOverlayClick; + + /// 抽屉的底部 + final Widget? footer; + + /// 抽屉里的列表项 + final List? items; + + /// 自定义内容,优先级高于[items]/[footer]/[title] + final Widget? contentWidget; + + /// 抽屉方向 + final TDDrawerPlacement? placement; + + /// 是否显示遮罩层 + final bool? showOverlay; + + /// 抽屉的标题 + final String? title; + + /// 抽屉的标题组件 + final Widget? titleWidget; + + /// 组件是否可见 + final bool? visible; + + /// 关闭时触发 + final VoidCallback? onClose; + + /// 点击抽屉里的列表项触发 + final TDDrawerItemClickCallback? onItemClick; + + /// 宽度 + final double? width; + + /// 距离顶部的距离 + final double? drawerTop; + + /// 列表自定义样式 + final TDCellStyle? style; + + /// 是否开启点击反馈 + final bool? hover; + + /// 组件背景颜色 + final Color? backgroundColor; + + /// 是否显示边框 + final bool? bordered; + + /// 是否显示最后一行分割线 + final bool? isShowLastBordered; + + TDSlidePopupRoute? _drawerRoute; + + void show() { + if (_drawerRoute != null) { + return; // 如果抽屉已经显示了,就不要再显示 + } + _drawerRoute = TDSlidePopupRoute( + slideTransitionFrom: placement == TDDrawerPlacement.right ? SlideTransitionFrom.right : SlideTransitionFrom.left, + isDismissible: (showOverlay ?? true) ? (closeOnOverlayClick ?? true) : false, + modalBarrierColor: (showOverlay ?? true) ? null : Colors.transparent, + modalTop: drawerTop, + builder: (context) { + return TDDrawerWidget( + footer: footer, + items: items, + contentWidget: contentWidget, + title: title, + titleWidget: titleWidget, + onItemClick: onItemClick, + width: width, + style: style, + hover: hover, + backgroundColor: backgroundColor, + bordered: bordered, + isShowLastBordered: isShowLastBordered, + ); + }, + ); + Navigator.of(context).push(_drawerRoute!).then((_) { + // 当抽屉关闭时,将_drawerRoute置为null + _deleteRouter(); + }); + } + + void open() { + show(); + } + + @mustCallSuper + void close() { + if (_drawerRoute != null) { + Navigator.of(context).pop(); + _deleteRouter(); + } + } + + void _deleteRouter() { + _drawerRoute = null; + onClose?.call(); + } +} diff --git a/tdesign-component/lib/src/components/drawer/td_drawer_widget.dart b/tdesign-component/lib/src/components/drawer/td_drawer_widget.dart new file mode 100644 index 000000000..fbeff44a6 --- /dev/null +++ b/tdesign-component/lib/src/components/drawer/td_drawer_widget.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; + +import '../../theme/td_colors.dart'; +import '../../theme/td_spacers.dart'; +import '../../theme/td_theme.dart'; +import '../cell/td_cell.dart'; +import '../cell/td_cell_group.dart'; +import '../cell/td_cell_style.dart'; +import 'td_drawer.dart'; + +typedef TDDrawerItemClickCallback = void Function(int index, TDDrawerItem item); + +/// 抽屉内容组件 +/// 可用于Scaffold中的drawer属性 +class TDDrawerWidget extends StatelessWidget { + const TDDrawerWidget({ + super.key, + this.footer, + this.items, + this.contentWidget, + this.title, + this.titleWidget, + this.onItemClick, + this.width = 280, + this.style, + this.hover = true, + this.backgroundColor, + this.bordered = true, + this.isShowLastBordered = true, + }); + + /// 抽屉的底部 + final Widget? footer; + + /// 抽屉里的列表项 + final List? items; + + /// 自定义内容,优先级高于[items]/[footer]/[title] + final Widget? contentWidget; + + /// 抽屉的标题 + final String? title; + + /// 抽屉的标题组件 + final Widget? titleWidget; + + /// 点击抽屉里的列表项触发 + final TDDrawerItemClickCallback? onItemClick; + + /// 宽度 + final double? width; + + /// 列表自定义样式 + final TDCellStyle? style; + + /// 是否开启点击反馈 + final bool? hover; + + /// 组件背景颜色 + final Color? backgroundColor; + + /// 是否显示边框 + final bool? bordered; + + /// 是否显示最后一行分割线 + final bool? isShowLastBordered; + + @override + Widget build(BuildContext context) { + var content = contentWidget; + if (content == null) { + var cellStyle = style; + if (cellStyle == null) { + cellStyle = TDCellStyle.cellStyle(context); + cellStyle.leftIconColor = TDTheme.of(context).fontGyColor1; + } + var cells = items + ?.asMap() + .map( + (index, item) => MapEntry( + index, + TDCell( + titleWidget: item.content, + title: item.title, + leftIconWidget: item.icon, + hover: hover, + bordered: bordered, + onClick: (cell) { + if (onItemClick == null) { + return; + } + onItemClick!(index, items![index]); + }, + ), + ), + ) + .values + .toList(); + content = Column( + children: [ + Expanded( + child: TDCellGroup( + title: title, + titleWidget: titleWidget, + style: cellStyle, + scrollable: true, + isShowLastBordered: isShowLastBordered, + cells: cells ?? [], + ), + ), + if (footer != null) + Container( + padding: EdgeInsets.all(TDTheme.of(context).spacer16), + child: footer, + ), + ], + ); + } + return Container( + color: backgroundColor ?? Colors.white, + width: width ?? 280, + height: double.infinity, + child: content, + ); + } + +} + +/// 抽屉里的列表项 +class TDDrawerItem { + TDDrawerItem({this.title, this.icon, this.content}); + + /// 每列标题 + final String? title; + + /// 每列图标 + final Widget? icon; + + /// 完全自定义 + final Widget? content; +} \ No newline at end of file diff --git a/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_inherited.dart b/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_inherited.dart new file mode 100644 index 000000000..7b9cd8f7e --- /dev/null +++ b/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_inherited.dart @@ -0,0 +1,22 @@ +import 'package:flutter/cupertino.dart'; + +import 'td_dropdown_item.dart'; +import 'td_dropdown_menu.dart'; +import 'td_dropdown_popup.dart'; + +class TDDropdownInherited extends InheritedWidget { + const TDDropdownInherited({required Widget child, required this.popupState, required this.directionListenable, Key? key}) + : super(child: child, key: key); + + final TDDropdownPopup popupState; + final ValueNotifier directionListenable; + + @override + bool updateShouldNotify(covariant TDDropdownInherited oldWidget) { + return true; + } + + static TDDropdownInherited? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } +} diff --git a/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_item.dart b/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_item.dart new file mode 100644 index 000000000..36466afcf --- /dev/null +++ b/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_item.dart @@ -0,0 +1,404 @@ +import 'dart:async'; +import 'dart:collection'; +import 'dart:math'; + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; +import '../../util/list_ext.dart'; +import '../tag/td_select_tag.dart'; +import '../tag/td_tag_styles.dart'; +import 'td_dropdown_inherited.dart'; +import 'td_dropdown_popup.dart'; + +typedef TDDropdownItemContentBuilder = Widget Function( + BuildContext context, _TDDropdownItemState itemState, TDDropdownPopup? popupState); + +List _getSelected(List? options) { + return options?.where((element) => element.selected == true).toList() ?? []; +} + +/// 补充列数,使最后一行的选项宽度一样 +int _num(List list, int? n) { + var val = n ?? 1; + if (list.length < val) { + return val; + } + return list.length + list.length % val; +} + +/// 下拉菜单内容 +class TDDropdownItem extends StatefulWidget { + const TDDropdownItem({ + Key? key, + this.disabled = false, + this.label, + this.arrowIcon, + this.multiple = false, + this.options = const [], + this.builder, + this.optionsColumns = 1, + this.onChange, + this.onConfirm, + this.onReset, + this.minHeight, + this.maxHeight, + this.tabBarWidth, + this.tabBarAlign, + this.tabBarFlex = 1, + }) : super(key: key); + + /// 是否禁用 + final bool? disabled; + + /// 标题 + final String? label; + + /// 自定义箭头图标 + final IconData? arrowIcon; + + /// 是否多选 + final bool? multiple; + + /// 选项数据 + final List? options; + + /// 完全自定义展示内容 + final TDDropdownItemContentBuilder? builder; + + /// 选项分栏(1-3) + final int? optionsColumns; + + /// 值改变时触发 + final ValueChanged? onChange; + + /// 点击确认时触发 + final ValueChanged? onConfirm; + + /// 点击重置时触发 + final VoidCallback? onReset; + + /// 内容最小高度 + final double? minHeight; + + /// 内容最大高度 + final double? maxHeight; + + /// 该item在menu上的宽度,仅在[TDDropdownMenu.isScrollable]为true时有效 + final double? tabBarWidth; + + /// [label]和[arrowIcon]/[TDDropdownMenu.arrowIcon]的对齐方式 + final MainAxisAlignment? tabBarAlign; + + /// 该item在menu上的宽度占比,仅在[TDDropdownMenu.isScrollable]为false时有效 + final int? tabBarFlex; + + static const double operateHeight = 73; + + double? get minContentHeight => + multiple == true ? (minHeight != null ? minHeight! + TDDropdownItem.operateHeight : null) : minHeight; + double? get maxContentHeight => + multiple == true ? (maxHeight != null ? maxHeight! + TDDropdownItem.operateHeight : null) : maxHeight; + + @override + _TDDropdownItemState createState() => _TDDropdownItemState(); + + String getLabel() { + if (multiple == true) { + return label ?? ''; + } + var list = _getSelected(options); + if (list.isEmpty) { + return label ?? ''; + } + return list[0]?.label ?? label ?? ''; + } +} + +class _TDDropdownItemState extends State { + late TDDropdownPopup popupState; + late ValueNotifier directionListenable; + + @override + Widget build(BuildContext context) { + popupState = TDDropdownInherited.of(context)!.popupState; + directionListenable = TDDropdownInherited.of(context)!.directionListenable; + if (widget.builder != null) { + return widget.builder!(context, this, popupState); + } + return widget.multiple == true || (widget.optionsColumns ?? 1) > 1 ? _getCheckboxList() : _getRadioList(); + } + + Widget _getCheckboxList() { + var isMultiple = widget.multiple == true; + var paddingNum = TDTheme.of(context).spacer16; + var groupChunk = _groupChunkOptions(); + var maxContentHeight = widget.maxContentHeight != null + ? widget.maxContentHeight! + : directionListenable.value == TDDropdownMenuDirection.auto + ? double.infinity + : max(popupState.maxContentHeight - TDDropdownItem.operateHeight, 0); + return Column( + children: [ + Container( + color: TDTheme.of(context).whiteColor1, + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: widget.minContentHeight ?? 0.0, maxHeight: maxContentHeight), + child: SingleChildScrollView( + child: Column( + children: List.generate(groupChunk.length, (index) { + var entry = groupChunk.entries.elementAt(index); + var chunks = entry.value; + var selectIds = _getSelected(widget.options).map((e) => e!.value).toList(); + return Column( + children: [ + groupChunk.length == 1 && entry.key == '__default__' + ? const SizedBox.shrink() + : Container( + width: double.infinity, + padding: EdgeInsets.only(left: paddingNum, top: paddingNum, right: paddingNum), + color: TDTheme.of(context).whiteColor1, + child: TDText(entry.key == '__default__' ? context.resource.other : entry.key), + ), + Container( + padding: EdgeInsets.all(paddingNum), + color: TDTheme.of(context).whiteColor1, + child: TDCheckboxGroupContainer( + selectIds: isMultiple + ? selectIds + : selectIds.isEmpty + ? [] + : [selectIds[0]], + onCheckBoxGroupChange: _handleSelectChange, + child: Column( + children: List.generate(chunks.length, (ri) { + var num = _num(chunks[ri], widget.optionsColumns); + return Padding( + padding: _getPadding(chunks.length, ri, 'bottom'), + child: Row( + children: List.generate(num, (ci) { + return Expanded( + child: Padding( + padding: _getPadding(num, ci, 'right'), + child: _getCheckboxItem(chunks[ri], ci), + ), + ); + }), + ), + ); + }), + ), + ), + ), + ], + ); + }), + ), + ), + ), + ), + if (isMultiple) _getCheckboxOperate(), + ], + ); + } + + Widget _getRadioList() { + var selected = _getSelected(widget.options); + var radios = TDRadioGroup( + onRadioGroupChange: _handleSelectChange, + radioCheckStyle: TDRadioStyle.check, + selectId: selected.isEmpty ? null : selected[0]?.value, + child: Column( + children: List.generate( + widget.options?.length ?? 0, + (index) => TDRadio( + id: widget.options![index].value, + title: widget.options![index].label, + selectColor: widget.options![index].selectedColor, + enable: !(widget.options![index].disabled ?? false), + ), + ), + ), + ); + return widget.minContentHeight != null || widget.maxContentHeight != null + ? Container( + color: TDTheme.of(context).whiteColor1, + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: widget.minContentHeight ?? 0.0, maxHeight: widget.maxContentHeight ?? double.infinity), + child: widget.maxContentHeight != null ? SingleChildScrollView(child: radios) : radios, + ), + ) + : radios; + } + + Widget? _getCheckboxItem(List cols, int index) { + var col = index >= cols.length ? null : cols[index]; + if (col == null) { + return null; + } + var enable = !(col.disabled ?? false); + return TDCheckbox( + id: col.value, + title: col.label, + enable: !(col.disabled ?? false), + selectColor: col.selectedColor, + disableColor: col.disabledColor, + customIconBuilder: (context, checked) => null, + customContentBuilder: (context, checked, content) => Container( + height: 40, + decoration: BoxDecoration( + color: enable + ? checked + ? TDTheme.of(context).brandLightColor + : TDTheme.of(context).grayColor1 + : TDTheme.of(context).grayColor2, + borderRadius: BorderRadius.all( + Radius.circular(TDTheme.of(context).radiusDefault), + ), + ), + child: Center( + child: TDText( + content, + textColor: enable + ? checked + ? TDTheme.of(context).brandColor7 + : TDTheme.of(context).fontGyColor1 + : TDTheme.of(context).fontGyColor4, + ), + ), + ), + ); + } + + Widget _getCheckboxOperate() { + return Container( + height: TDDropdownItem.operateHeight, + padding: EdgeInsets.all(TDTheme.of(context).spacer16), + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + border: Border( + top: BorderSide( + color: TDTheme.of(context).grayColor3, + width: 1, + ), + bottom: directionListenable.value == TDDropdownMenuDirection.up + ? BorderSide( + color: TDTheme.of(context).grayColor3, + width: 1, + ) + : BorderSide.none, + ), + ), + child: Row(children: [ + Expanded( + child: TDButton( + text: context.resource.reset, + theme: TDButtonTheme.light, + onTap: () { + widget.options?.forEach((element) { + element.selected = false; + }); + setState(() {}); + widget.onReset?.call(); + }, + ), + ), + SizedBox(width: TDTheme.of(context).spacer16), + Expanded( + child: TDButton( + text: context.resource.confirm, + theme: TDButtonTheme.primary, + onTap: () { + _handleClose(); + widget.onConfirm?.call(_getSelected(widget.options).map((e) => e!.value).toList()); + }, + ), + ), + ]), + ); + } + + EdgeInsets _getPadding(int length, int index, String direction) { + var value = length - 1 == index ? 0.0 : TDTheme.of(context).spacer12; + if (direction == 'bottom') { + return EdgeInsets.only(bottom: value); + } + if (direction == 'right') { + return EdgeInsets.only(right: value); + } + return EdgeInsets.all(value); + } + + Map>> _groupChunkOptions() { + var groupedOptions = widget.options?.groupBy((option) => option.group ?? '__default__') ?? {}; + var groupedChunkOptions = >>{}; + var def = groupedOptions.remove('__default__'); + if (def != null) { + groupedOptions['__default__'] = def; + } + groupedOptions.forEach((key, value) { + groupedChunkOptions[key] = value.chunk(widget.optionsColumns ?? 1); + }); + return groupedChunkOptions; + } + + void _handleSelectChange(selected) { + var isRadio = widget.multiple != true && selected is List; + if (isRadio && selected.isNotEmpty) { + selected = [selected.last]; + } + widget.options?.forEach((element) { + element.selected = selected is List ? selected.contains(element.value) : element.value == selected; + }); + if (isRadio) { + setState(() {}); + } + widget.onChange?.call(_getSelected(widget.options).map((e) => e!.value).toList()); + if (widget.multiple != true && selected.isNotEmpty) { + _handleClose(); + } + } + + void _handleClose() async { + if (widget.multiple != true || (widget.optionsColumns ?? 1) > 1) { + await Future.delayed(const Duration(milliseconds: 100)); + } + await Navigator.maybePop(context); + } +} + +/// 选项数据 +class TDDropdownItemOption { + TDDropdownItemOption({ + required this.value, + required this.label, + this.disabled = false, + this.group, + this.selected = false, + this.selectedColor, + this.disabledColor + }); + + /// 选项值 + final String value; + + /// 选项标题 + final String label; + + /// 是否禁用 + final bool? disabled; + + /// 分组,相同的为一组 + final String? group; + + /// 是否选中 + late bool? selected; + + /// 选中颜色 + final Color? selectedColor; + + /// 禁用颜色 + final Color? disabledColor; +} diff --git a/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_menu.dart b/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_menu.dart new file mode 100644 index 000000000..278ba9f51 --- /dev/null +++ b/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_menu.dart @@ -0,0 +1,309 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import '../../theme/td_colors.dart'; +import '../../theme/td_fonts.dart'; +import '../../theme/td_theme.dart'; +import '../icon/td_icons.dart'; +import '../text/td_text.dart'; +import './td_dropdown_item.dart'; +import 'td_dropdown_popup.dart'; + +/// 菜单展开方向 +enum TDDropdownMenuDirection { + /// 向下 + down, + + /// 向上 + up, + + /// 根据内容高度动态展示方向 + auto, +} + +/// 下拉菜单构建器 +typedef TDDropdownItemBuilder = List Function(BuildContext context); + +/// 自定义标签内容 +typedef LabelBuilder = Widget Function(BuildContext context, String label, bool isOpened, int index); + +/// 下拉菜单 +class TDDropdownMenu extends StatefulWidget { + const TDDropdownMenu({ + Key? key, + this.builder, + this.items, + this.closeOnClickOverlay = true, + this.direction = TDDropdownMenuDirection.auto, + this.duration = 200.0, + this.showOverlay = true, + this.isScrollable = false, + this.arrowIcon, + this.labelBuilder, + this.onMenuOpened, + this.onMenuClosed, + this.width, + this.height = 48, + this.tabBarAlign = MainAxisAlignment.center, + this.decoration, + }) : super(key: key); + + /// 下拉菜单构建器,优先级高于[items] + final TDDropdownItemBuilder? builder; + + /// 下拉菜单 + final List? items; + + /// 是否在点击遮罩层后关闭菜单 + final bool? closeOnClickOverlay; + + /// 菜单展开方向(down、up、auto) + final TDDropdownMenuDirection? direction; + + /// 动画时长,毫秒 + final double? duration; + + /// 是否显示遮罩层 + final bool? showOverlay; + + /// 自定义箭头图标 + final IconData? arrowIcon; + + /// 自定义标签内容 + final LabelBuilder? labelBuilder; + + /// 展开菜单事件 + final ValueChanged? onMenuOpened; + + /// 关闭菜单事件 + final ValueChanged? onMenuClosed; + + /// 是否开启滚动列表 + final bool? isScrollable; + + /// menu的宽度 + final double? width; + + /// menu的高度 + final double? height; + + /// [TDDropdownItem.label]和[arrowIcon]/[TDDropdownItem.arrowIcon]的对齐方式 + final MainAxisAlignment? tabBarAlign; + + /// 下拉菜单的装饰器 + final Decoration? decoration; + + @override + _TDDropdownMenuState createState() => _TDDropdownMenuState(); +} + +class _TDDropdownMenuState extends State with TickerProviderStateMixin { + List? _items; + List? _iconControllers; + late List> _iconAnimations; + late List _isOpened; + TDDropdownPopup? _dropdownPopup; + + @override + void initState() { + super.initState(); + _init(); + } + + @override + void dispose() { + _iconControllers?.forEach((controller) { + controller.dispose(); + }); + super.dispose(); + } + + @override + void didUpdateWidget(TDDropdownMenu oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.builder != oldWidget.builder || widget.items != oldWidget.items) { + _init(); + } + } + + @override + Widget build(BuildContext context) { + var tabBar = widget.isScrollable == true + ? SingleChildScrollView( + scrollDirection: Axis.horizontal, + physics: const BouncingScrollPhysics(), + child: Row( + children: List.generate( + _items?.length ?? 0, + (index) => SizedBox( + width: _items![index].tabBarWidth, + child: _tabBarContent(index), + ), + ), + ), + ) + : Row( + children: List.generate( + _items?.length ?? 0, + (index) { + return Expanded( + flex: _items![index].tabBarFlex ?? 1, + child: _tabBarContent(index), + ); + }, + ), + ); + return Container( + height: widget.height, + width: widget.width ?? double.infinity, + decoration: widget.decoration ?? + BoxDecoration( + color: TDTheme.of(context).whiteColor1, + border: Border( + bottom: BorderSide( + color: TDTheme.of(context).grayColor3, + width: 1, + ), + ), + ), + child: tabBar, + ); + } + + void _init() { + var items = widget.builder?.call(context) ?? widget.items ?? []; + if (items.length == _items?.length) { + _items = items; + return; + } + _isOpened = List.filled(items.length, false); + _items = items; + _iconControllers?.forEach((controller) { + controller.dispose(); + }); + _iconControllers = List.generate( + _items?.length ?? 0, + (index) => AnimationController( + duration: Duration(milliseconds: (widget.duration ?? 200).toInt()), + vsync: this, + )); + _iconAnimations = _iconControllers?.map((e) => Tween(begin: 0, end: 0.5).animate(e)).toList() ?? []; + } + + Widget _tabBarContent(int index) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () async { + if (_disabled(index)) { + return; + } + _isOpened[index] ? await Navigator.maybePop(context) : _openMenu(index); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: _items![index].tabBarAlign ?? widget.tabBarAlign ?? MainAxisAlignment.center, + children: [Flexible(child: _getText(index)), _getIcon(index)], + ), + ); + } + + Widget _getText(int index) { + final label = _items![index].getLabel(); + if (widget.labelBuilder != null) { + return widget.labelBuilder!(context, label, _isOpened[index], index); + } + var textColor = _disabled(index) + ? TDTheme.of(context).fontGyColor4 + : _isOpened[index] + ? TDTheme.of(context).brandColor7 + : TDTheme.of(context).fontGyColor1; + return TDText( + label, + font: TDTheme.of(context).fontBodyMedium, + textColor: textColor, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ); + } + + Widget _getIcon(int index) { + var arrowIcon = _items![index].arrowIcon ?? + widget.arrowIcon ?? + (widget.direction == TDDropdownMenuDirection.up ? TDIcons.caret_up_small : TDIcons.caret_down_small); + return RotationTransition( + turns: _iconAnimations[index], + child: Icon( + arrowIcon, + size: 24, + color: _disabled(index) + ? TDTheme.of(context).fontGyColor4 + : _isOpened[index] + ? TDTheme.of(context).brandColor7 + : null, + ), + ); + } + + bool _disabled(int index) { + return _items![index].disabled == true; + } + + Future openMenu(int index) async { + await _openMenu(index); + } + + Future closeMenu() async { + await _closeMenu(); + } + + /// 打开菜单 + Future _openMenu(int index) async { + /// 如果已经打开了,则关闭 + if (_isOpened.contains(true)) { + await Navigator.maybePop(context); + } + _dropdownPopup ??= TDDropdownPopup( + child: _items![index], + parentContext: context, + handleClose: _closeMenu, + direction: widget.direction, + showOverlay: widget.showOverlay, + closeOnClickOverlay: widget.closeOnClickOverlay, + duration: Duration(milliseconds: (widget.duration ?? 200).toInt()), + ); + unawaited(_dropdownPopup!.add(_items![index]).then((value) { + widget.onMenuOpened?.call(index); + })); + + _isOpened = List.filled(_items?.length ?? 0, false); + _isOpened[index] = true; + setState(() {}); + _iconControllers?.asMap().forEach((key, value) { + if (value.status == AnimationStatus.completed) { + value.reverse(); + } else if (key == index) { + value.forward(); + } + }); + } + + /// 关闭菜单 + Future _closeMenu() async { + var index = _isOpened.indexOf(true); + if (index < 0) { + return; + } + _isOpened = List.filled(_items?.length ?? 0, false); + setState(() {}); + _iconControllers?.forEach((value) { + if (value.status == AnimationStatus.completed) { + value.reverse(); + } + }); + await _dropdownPopup?.remove(); + if (index >= 0 && widget.onMenuClosed != null) { + widget.onMenuClosed!(index); + } + } +} diff --git a/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_panel.dart b/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_panel.dart new file mode 100644 index 000000000..9636bdfc1 --- /dev/null +++ b/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_panel.dart @@ -0,0 +1,142 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:flutter/material.dart'; + +import 'td_dropdown_menu.dart'; +import 'td_dropdown_popup.dart'; + +class TDDropdownPanel extends StatefulWidget { + const TDDropdownPanel({ + Key? key, + required this.initContentTop, + required this.initContentBottom, + required this.reverseHeight, + required this.duration, + required this.directionListenable, + required this.colorAlphaListenable, + required this.direction, + required this.closeListenable, + required this.onOpened, + required this.child, + }) : super(key: key); + + final double initContentTop; + final double initContentBottom; + final double reverseHeight; + final Duration duration; + final ValueNotifier directionListenable; + final ValueNotifier colorAlphaListenable; + final TDDropdownPopupDirection direction; + final ValueNotifier closeListenable; + final VoidCallback onOpened; + final Widget child; + + @override + _TDDropdownPanelState createState() => _TDDropdownPanelState(); +} + +class _TDDropdownPanelState extends State with SingleTickerProviderStateMixin { + double? contentTop, contentBottom; + late AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController(vsync: this, duration: widget.duration); + widget.closeListenable.value = close; + } + + @override + void didUpdateWidget(TDDropdownPanel oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.directionListenable != oldWidget.directionListenable) { + widget.closeListenable.value = close; + } + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return PositionedTransition( + rect: _getAnimation(), + child: SingleChildScrollView( + child: Builder( + builder: (BuildContext context) { + open(context); + return widget.child; + }, + ), + ), + ); + } + + void open(BuildContext itemContext) { + if (contentBottom != null || contentTop != null) { + return; + } + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + var renderBox = itemContext.findRenderObject() as RenderBox; + var size = renderBox.size; + if (widget.directionListenable.value == TDDropdownPopupDirection.auto) { + // 比较展开方向(down)的高度能不能放下item,能将方向更新为down + // 否则比较反方向(up)的高度是否大于down的方向,大于则将方向更新为up,否则保持为down + if (widget.direction == TDDropdownPopupDirection.down) { + if (widget.initContentBottom >= size.height) { + widget.directionListenable.value = TDDropdownPopupDirection.down; + } else { + if (widget.reverseHeight > widget.initContentBottom) { + widget.directionListenable.value = TDDropdownPopupDirection.up; + } else { + widget.directionListenable.value = TDDropdownPopupDirection.down; + } + } + } else { + if (widget.initContentTop >= size.height) { + widget.directionListenable.value = TDDropdownPopupDirection.up; + } else { + if (widget.reverseHeight > widget.initContentTop) { + widget.directionListenable.value = TDDropdownPopupDirection.down; + } else { + widget.directionListenable.value = TDDropdownPopupDirection.up; + } + } + } + return; + } + if (widget.direction == TDDropdownPopupDirection.down) { + contentBottom = widget.initContentBottom - size.height; + } else { + contentTop = widget.initContentTop - size.height; + } + setState(() {}); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_controller.status == AnimationStatus.dismissed) { + widget.colorAlphaListenable.value = true; + _controller.duration = widget.duration; + _controller.forward().whenCompleteOrCancel(() { + widget.onOpened(); + }); + } + }); + }); + } + + Animation _getAnimation() { + return RelativeRectTween( + begin: RelativeRect.fromLTRB(0, widget.initContentTop, 0, widget.initContentBottom), + end: RelativeRect.fromLTRB(0, contentTop ?? widget.initContentTop, 0, contentBottom ?? widget.initContentBottom), + ).animate(_controller); + } + + Future close() { + widget.colorAlphaListenable.value = false; + _controller.duration = widget.duration ~/ 2; + return _controller.reverse(); + } +} diff --git a/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_popup.dart b/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_popup.dart new file mode 100644 index 000000000..6bf2ce687 --- /dev/null +++ b/tdesign-component/lib/src/components/dropdown_menu/td_dropdown_popup.dart @@ -0,0 +1,218 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import 'td_dropdown_inherited.dart'; +import 'td_dropdown_item.dart'; +import 'td_dropdown_menu.dart'; +import 'td_dropdown_panel.dart'; + +typedef TDDropdownPopupDirection = TDDropdownMenuDirection; +typedef FutureCallback = Future Function(); + +class TDDropdownPopup { + TDDropdownPopup({ + required this.parentContext, + required this.child, + required this.handleClose, + this.direction = TDDropdownPopupDirection.auto, + this.showOverlay = true, + this.closeOnClickOverlay = true, + this.duration = const Duration(milliseconds: 200), + }); + + final BuildContext parentContext; + final TDDropdownItem child; + final FutureCallback handleClose; + final TDDropdownPopupDirection? direction; + final bool? showOverlay; + final bool? closeOnClickOverlay; + final Duration? duration; + + /// _overlay1:下拉方向的 + late double _overlay1Top, + _overlay1Bottom, + + /// _overlay2:menu部分的 + _overlay2Top, + _overlay2Bottom, + + /// _overlay3:下拉反方向的 + _overlay3Top, + _overlay3Bottom, + + /// _overlay3Height:下拉反方向的高度,用于判断auto方向 + _overlay3Height, + + /// _initContent:初始内容 + _initContentTop, + _initContentBottom; + final _closeListenable = ValueNotifier(null); + final _directionListenable = ValueNotifier(TDDropdownPopupDirection.auto); + final _colorAlphaListenable = ValueNotifier(false); + + Duration get _duration => duration ?? const Duration(milliseconds: 200); + + double get maxContentHeight => direction == TDDropdownPopupDirection.down ? _initContentBottom : _initContentTop; + + void _init(TDDropdownPopupDirection d) { + final ancestor = Navigator.of(parentContext).context.findRenderObject(); + final popupContainerHeight = (ancestor as RenderBox).size.height; + var renderBox = parentContext.findRenderObject() as RenderBox; + var position = renderBox.localToGlobal(Offset.zero, ancestor: ancestor); + var size = renderBox.size; + if (d == TDDropdownPopupDirection.down) { + _overlay1Top = position.dy + size.height; + _overlay2Top = position.dy; + _overlay3Top = 0; + _initContentTop = position.dy + size.height; + + _overlay1Bottom = 0; + _overlay2Bottom = popupContainerHeight - position.dy - size.height; + _overlay3Bottom = popupContainerHeight - position.dy; + + _overlay3Height = position.dy; + _initContentBottom = popupContainerHeight - position.dy - size.height; + } else { + _overlay1Top = 0; + _overlay2Top = position.dy; + _overlay3Top = position.dy + size.height; + _initContentTop = position.dy; + + _overlay1Bottom = popupContainerHeight - position.dy; + _overlay2Bottom = popupContainerHeight - position.dy - size.height; + _overlay3Bottom = 0; + + _overlay3Height = popupContainerHeight - position.dy - size.height; + _initContentBottom = popupContainerHeight - position.dy; + } + } + + Future add([TDDropdownItem? updateChild]) { + var completer = Completer(); + _directionListenable.value = direction ?? TDDropdownPopupDirection.auto; + final overlayEntry = OverlayEntry( + builder: (BuildContext context) { + return _directionListenable.value == TDDropdownPopupDirection.auto + ? ValueListenableBuilder( + valueListenable: _directionListenable, + builder: (context, value, child) => value == TDDropdownPopupDirection.auto + ? child! + : _getPopup(value, updateChild, completer), // 每次重新渲染item,更新高度 + child: _getPopup(TDDropdownPopupDirection.down, updateChild, completer), + ) + : _getPopup(_directionListenable.value, updateChild, completer); + }, + ); + Navigator.push(parentContext, _PopupOverlayRoute(overlayEntry, handleClose)); + return completer.future; + } + + Widget _getPopup(TDDropdownMenuDirection value, TDDropdownItem? updateChild, Completer completer) { + _init(value); + final barrier = GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: _overlayClick, + ); + return Stack(children: [ + if (_directionListenable.value != TDDropdownPopupDirection.auto) ...[ + _getOverlay1(barrier), + _getOverlay2(), + _getOverlay3(barrier), + ], + TDDropdownInherited( + popupState: this, + directionListenable: _directionListenable, + child: TDDropdownPanel( + duration: _duration, + direction: value, + directionListenable: _directionListenable, + colorAlphaListenable: _colorAlphaListenable, + initContentBottom: _initContentBottom, + initContentTop: _initContentTop, + reverseHeight: _overlay3Height, + closeListenable: _closeListenable, + onOpened: () { + completer.complete(); + }, + child: updateChild ?? child, + ), + ), + ]); + } + + Widget _getOverlay1(Widget barrier) { + return Positioned( + top: _overlay1Top, + bottom: _overlay1Bottom, + left: 0, + right: 0, + child: showOverlay == true + ? ValueListenableBuilder( + builder: (BuildContext context, value, Widget? child) { + return AnimatedContainer( + color: value ? Colors.black54 : Colors.black54.withAlpha(0), + duration: value ? _duration : _duration ~/ 2, + child: barrier, + ); + }, + valueListenable: _colorAlphaListenable, + ) + : barrier, + ); + } + + Widget _getOverlay2() { + return Positioned( + top: _overlay2Top, + bottom: _overlay2Bottom, + left: 0, + right: 0, + child: GestureDetector( + onVerticalDragUpdate: (details) {}, + onHorizontalDragUpdate: (details) {}, + behavior: HitTestBehavior.translucent, + ), + ); + } + + Widget _getOverlay3(Widget barrier) { + return Positioned( + top: _overlay3Top, + bottom: _overlay3Bottom, + left: 0, + right: 0, + child: barrier, + ); + } + + void _overlayClick() { + if (!(closeOnClickOverlay ?? true)) { + return; + } + Navigator.maybePop(parentContext); + } + + Future remove() async { + await _closeListenable.value?.call(); + _closeListenable.value = null; + } +} + +class _PopupOverlayRoute extends OverlayRoute { + final OverlayEntry overlayEntry; + final FutureCallback handleClose; + + _PopupOverlayRoute(this.overlayEntry, this.handleClose); + + @override + Iterable createOverlayEntries() { + return [overlayEntry]; + } + + @override + Future willPop() async { + await handleClose(); + return super.willPop(); + } +} diff --git a/tdesign-component/lib/src/components/empty/td_empty.dart b/tdesign-component/lib/src/components/empty/td_empty.dart new file mode 100644 index 000000000..b24ab3146 --- /dev/null +++ b/tdesign-component/lib/src/components/empty/td_empty.dart @@ -0,0 +1,80 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +typedef TDTapEvent = void Function(); + +enum TDEmptyType { plain, operation } + +class TDEmpty extends StatelessWidget { + const TDEmpty( + {this.type = TDEmptyType.plain, + this.image, + this.emptyText, + this.operationText, + this.operationTheme, + this.onTapEvent, + this.emptyTextColor, + this.emptyTextFont, + this.customOperationWidget, + Key? key}) + : super(key: key); + + /// 点击事件 + final TDTapEvent? onTapEvent; + /// 展示图片 + final Widget? image; + /// 描述文字 + final String? emptyText; + /// 描述文字颜色 + final Color? emptyTextColor; + /// 描述文字大小 + final Font? emptyTextFont; + /// 操作按钮文案 + final String? operationText; + /// 操作按钮文案主题色 + final TDButtonTheme? operationTheme; + /// 类型,为operation有操作按钮,plain无按钮 + final TDEmptyType type; + /// 自定义操作按钮 + final Widget? customOperationWidget; + + @override + Widget build(BuildContext context) { + return Container( + alignment: Alignment.center, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + image ?? Icon( + TDIcons.info_circle_filled, + size: 96, + color: TDTheme.of(context).fontGyColor3, + ), + Padding(padding: EdgeInsets.only(top: image == null ? 22 : 16)), + TDText( + emptyText ?? '', + fontWeight: FontWeight.w400, + font: emptyTextFont??TDTheme.of(context).fontBodyMedium, + textColor: emptyTextColor??TDTheme.of(context).fontGyColor2.withOpacity(0.6), + ), + (type == TDEmptyType.operation) + ? customOperationWidget ?? Padding( + padding: const EdgeInsets.only(top: 32), + child: TDButton( + text: operationText ?? '', + size: TDButtonSize.large, + theme: operationTheme??TDButtonTheme.primary, + width: 179, + onTap: () { + if (onTapEvent != null) { + onTapEvent!(); + } + }, + )) + : Container() + ], + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/fab/td_fab.dart b/tdesign-component/lib/src/components/fab/td_fab.dart new file mode 100644 index 000000000..57143edc0 --- /dev/null +++ b/tdesign-component/lib/src/components/fab/td_fab.dart @@ -0,0 +1,218 @@ + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + + +enum TDFabTheme { primary, defaultTheme, light, danger } + +enum TDFabShape { + circle, // 圆形 + square // 矩形 +} + +enum TDFabSize { + large, // 大 + medium, // 中 + small, // 小 + extraSmall // 特小 +} + +class TDFab extends StatelessWidget { + const TDFab( + {Key? key, + this.theme = TDFabTheme.defaultTheme, + this.shape = TDFabShape.circle, + this.size = TDFabSize.large, + this.text, + this.onClick, + this.icon}) + : super(key: key); + + /// 主题 + final TDFabTheme theme; + + /// 形状 + final TDFabShape shape; + + /// 大小 + final TDFabSize size; + + /// 文本 + final String? text; + + /// 图标 + final Icon? icon; + + /// 点击事件 + final VoidCallback? onClick; + + bool get showText => text != null && text != ''; + + EdgeInsets getPadding() { + switch (size) { + case TDFabSize.large: + return showText + ? const EdgeInsets.symmetric(horizontal: 20, vertical: 12) + : const EdgeInsets.all(12); + case TDFabSize.medium: + return showText + ? const EdgeInsets.symmetric(horizontal: 16, vertical: 8) + : const EdgeInsets.all(10); + case TDFabSize.small: + return showText + ? const EdgeInsets.symmetric(horizontal: 12, vertical: 5) + : const EdgeInsets.all(7); + case TDFabSize.extraSmall: + return showText + ? const EdgeInsets.symmetric(horizontal: 8, vertical: 3) + : const EdgeInsets.all(5); + default: + return showText + ? const EdgeInsets.symmetric(horizontal: 20, vertical: 12) + : const EdgeInsets.all(12); + } + } + + double getMinWidthOrHeight() { + switch (size) { + case TDFabSize.large: + return 48.0; + case TDFabSize.medium: + return 40.0; + case TDFabSize.small: + return 32.0; + case TDFabSize.extraSmall: + return 28.0; + default: + return 48.0; + } + } + + Color getBackgroundColor(BuildContext context) { + switch (theme) { + case TDFabTheme.primary: + return TDTheme.of(context).brandColor7; + case TDFabTheme.defaultTheme: + return TDTheme.of(context).grayColor3; + case TDFabTheme.light: + return TDTheme.of(context).brandColor1; + case TDFabTheme.danger: + return TDTheme.of(context).errorColor6; + default: + return TDTheme.of(context).grayColor3; + } + } + + Color getIconColor(BuildContext context) { + switch (theme) { + case TDFabTheme.primary: + return Colors.white; + case TDFabTheme.defaultTheme: + return TDTheme.of(context).fontGyColor1.withOpacity(0.9); + case TDFabTheme.light: + return TDTheme.of(context).brandColor7; + case TDFabTheme.danger: + return Colors.white; + default: + return TDTheme.of(context).fontGyColor1.withOpacity(0.9); + } + } + + double getIconSize() { + switch (size) { + case TDFabSize.large: + return 24.0; + case TDFabSize.medium: + return 20.0; + case TDFabSize.small: + return 18.0; + case TDFabSize.extraSmall: + return 18.0; + default: + return 24.0; + } + } + + double getFontSize() { + switch (size) { + case TDFabSize.large: + return 16.0; + case TDFabSize.medium: + return 16.0; + case TDFabSize.small: + return 14.0; + case TDFabSize.extraSmall: + return 14.0; + default: + return 16.0; + } + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + if (onClick != null) { + onClick!(); + } + }, + child: InkWell( + child: Container( + padding: getPadding(), + decoration: BoxDecoration( + color: getBackgroundColor(context), + boxShadow: [ + BoxShadow( + offset: const Offset(0, 5), + blurRadius: 2.5, + spreadRadius:-1.5, + color: Colors.black.withOpacity(0.1)), + BoxShadow( + offset: const Offset(0, 8), + blurRadius: 5, + spreadRadius:0.5, + color: Colors.black.withOpacity(0.06)), + BoxShadow( + offset: const Offset(0, 3), + blurRadius: 7, + spreadRadius:1, + color: Colors.black.withOpacity(0.05)) + ], + borderRadius: shape == TDFabShape.circle + ? BorderRadius.circular(24) + : BorderRadius.circular(6)), + height: getMinWidthOrHeight(), + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + icon ?? + Icon( + TDIcons.add, + size: getIconSize(), + color: getIconColor(context), + ), + Visibility( + visible: showText, + child: const SizedBox( + width: 4, + )), + Visibility( + visible: showText, + child: TDText( + text ?? '', + style: TextStyle( + height: 1.5, + fontWeight: FontWeight.w600, + fontSize: getFontSize(), + color: getIconColor(context), + leadingDistribution: TextLeadingDistribution.even), + )) + ], + ), + ), + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/footer/td_footer.dart b/tdesign-component/lib/src/components/footer/td_footer.dart new file mode 100644 index 000000000..33d077bb2 --- /dev/null +++ b/tdesign-component/lib/src/components/footer/td_footer.dart @@ -0,0 +1,143 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +enum TDFooterType { + /// 文字样式 + text, + + /// 链接样式 + link, + + /// 品牌样式 + brand, +} + +class TDFooter extends StatefulWidget { + const TDFooter( + this.type, { + Key? key, + this.logo, + this.text = '', + this.links = const [], + this.width, + this.height, + }) : super(key: key); + + /// 品牌图片 + final String? logo; + + /// 样式 + final TDFooterType type; + + /// 文字 + final String text; + + /// 自定义图片宽 + final double? width; + + /// 自定义图片高 + final double? height; + + /// 链接 + final List links; + + @override + State createState() => _TDFooterState(); +} + +class _TDFooterState extends State { + @override + Widget build(BuildContext context) { + switch (widget.type) { + case TDFooterType.text: + return Container( + alignment: Alignment.center, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + _renderText(), + ], + ), + ); + case TDFooterType.link: + return Container( + alignment: Alignment.center, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (widget.links.isNotEmpty) _renderLinks() else _renderText(), + ], + ), + ); + case TDFooterType.brand: + return Container( + alignment: Alignment.center, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (widget.logo != null) _renderLogo() else _renderText(), + ], + ), + ); + } + } + + Widget _renderLogo() { + return Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + Padding( + padding: const EdgeInsets.only(top: 4, bottom: 4), + child: TDImage( + assetUrl: widget.logo, + type: TDImageType.fitWidth, + width: widget.width, + height: widget.height, + ), + ) + ]); + } + + Widget _renderLinks() { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(top: 4, bottom: 4), + child: Wrap( + alignment: WrapAlignment.center, + children: List.generate(widget.links.length, (index) { + var link = widget.links[index]; + return Container( + decoration: index < (widget.links.length - 1) + ? BoxDecoration( + border: Border( + right: BorderSide( + color: TDTheme.of(context).grayColor3))) + : null, + padding: const EdgeInsets.symmetric(horizontal: 6), + child: link, + ); + }).toList(), + ), + ), + Padding( + padding: const EdgeInsets.only(bottom: 4), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [Flexible(child: _renderText())]), + ), + ], + ); + } + + Widget _renderText() { + return Text( + widget.text, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + color: TDTheme.of(context).fontGyColor3, + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/form/td_form.dart b/tdesign-component/lib/src/components/form/td_form.dart new file mode 100644 index 000000000..ecb549548 --- /dev/null +++ b/tdesign-component/lib/src/components/form/td_form.dart @@ -0,0 +1,203 @@ + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import 'td_form_inherited.dart'; + +class TDForm extends StatefulWidget { + const TDForm( + {Key? key, + required this.items, + required this.rules, + required this.onSubmit, + required this.data, + this.colon = false, + this.formContentAlign = TextAlign.left, + this.isHorizontal = true, + this.disabled = false, + this.errorMessage, + this.formLabelAlign = TextAlign.left, + this.labelWidth = 20.0, + this.preventSubmitDefault = true, + this.requiredMark = true, // 此处必填项有小问题 + this.scrollToFirstError, + this.formShowErrorMessage = true, + this.submitWithWarningMessage = false, + this.onReset, + this.formController, + this.btnGroup}) + : super(key: key); + + /// 表单内容 items + final List items; + + /// 是否在表单标签字段右侧显示冒号 + final bool? colon; + + /// 表单内容对齐方式: 左对齐、右对齐、居中对齐 + /// 可选项: left/right/center + /// 默认为左对齐 + /// 优先级低于 TDFormItem 的对齐 API + /// TODO: TDStepper TDRate 等组件没用实现通用性 + final TextAlign formContentAlign; + + /// 表单数据 + final Map data; + + /// 表单排列方式是否为 水平方向 + final bool isHorizontal; + + /// 是否禁用整个表单 + final bool disabled; + + /// 表单信息错误信息配置 + final Object? errorMessage; + + /// 表单字段标签的对齐方式: + /// 左对齐、右对齐、顶部对齐 + /// 可选项: left/right/top + /// TODO: 表单总体标签对齐方式 + final TextAlign? formLabelAlign; + + /// 可以整体设置 label 标签宽度 + final double? labelWidth; + + /// 是否阻止表单提交默认事件(表单提交默认事件会刷新页面) + /// 设置为 true 可以避免刷新 + final bool? preventSubmitDefault; + + /// 是否显示必填符号(*),默认显示 + final bool? requiredMark; + + /// 整个表单字段校验规则 + final Map rules; + + /// 表单校验不通过时,是否自动滚动到第一个校验不通过的字段,平滑滚动或是瞬间直达。 + /// 值为空则表示不滚动。可选项:''/smooth/auto + final String? scrollToFirstError; + + /// 校验不通过时,是否显示错误提示信息,统一控制全部表单项 + /// 如果希望控制单个表单项,请给 FormItem 设置该属性 + final bool? formShowErrorMessage; + + /// 【讨论中】当校验结果只有告警信息时,是否触发 submit 提交事件 + final bool? submitWithWarningMessage; + + /// 表单提交时触发 + final Function onSubmit; + + /// 表单重置时触发 + final Function? onReset; + + /// 表单按钮组 + final List? btnGroup; + + /// 表单控制器 + final FormController? formController; + @override + State createState() => _TDFormState(); +} + +class _TDFormState extends State { + List _formItems = []; + Map _formData = {}; + bool _isValidate = false; + bool _isReset=false; + //用于更新表单 + int _upDataCount=1; + @override + void initState() { + super.initState(); + _formData = widget.data; + if (widget.formController != null) { + widget.formController?.addListener((){ + if(widget.formController?.eventType=='submit'){ + onSubmit(); + }else if(widget.formController?.eventType=='reset'){ + onReset(); + } + }); + } + } + onReset(){ + _upDataCount+=1; + setState(() { + _formData=widget.formController!.formData; + _isReset=true; + }); + } + onSubmit() { + _upDataCount+=1; + _isReset=false; + bool isValidateSuc = true; + _formData.forEach((key, value) { + if(isValidateSuc){ + isValidateSuc = validate(key, '${value}'); + } + }); + if (!isValidateSuc) { + setState(() { + _isValidate =true; + }); + } + widget.onSubmit(_formData, isValidateSuc); + } + + ///检验表单数据 + bool validate(name, value) { + if(widget.rules[name]!=null){ + final result = widget.rules[name]!.check(value); + if (result != null) { + /// 返回第一个不通过的错误信息 + return false; + } + } + return true; + } + + @override + Widget build(BuildContext context) { + _formItems = widget.items.expand((item) => [item, SizedBox(height: 1)]).toList(); + if (widget.btnGroup!.isNotEmpty) { + _formItems.addAll(widget.btnGroup ?? []); + } + return TDFormInherited( + formData: widget.data, + labelWidth: widget.labelWidth, + isHorizontal: widget.isHorizontal, + isValidate: _isValidate, + rules: widget.rules, + formContentAlign: widget.formContentAlign, + formShowErrorMessage: widget.formShowErrorMessage, + requiredMark: widget.requiredMark, + updataCount: _upDataCount, + onFormDataChange: (value) { + ///监听表单数据变化 + _formData = value; + }, + isReset:_isReset, + onSubmit: onSubmit, + child: ListView.builder( + itemCount: _formItems.length, + itemBuilder: (context, index) => _formItems[index], + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + cacheExtent: 500, + ), + ); + } +} + +class FormController with ChangeNotifier { + String eventType=''; + Map formData={}; + submit() { + eventType='submit'; + notifyListeners(); + } + reset(Map data){ + formData=data; + eventType='reset'; + notifyListeners(); + } +} diff --git a/tdesign-component/lib/src/components/form/td_form_inherited.dart b/tdesign-component/lib/src/components/form/td_form_inherited.dart new file mode 100644 index 000000000..28193c887 --- /dev/null +++ b/tdesign-component/lib/src/components/form/td_form_inherited.dart @@ -0,0 +1,50 @@ +import 'package:flutter/cupertino.dart'; + +import '../../../tdesign_flutter.dart'; + +class TDFormInherited extends InheritedWidget { + final Map formData; + final double? labelWidth; + final bool isHorizontal; + final bool isValidate; + final Map rules; + final bool? formShowErrorMessage; + final bool? requiredMark; + final TextAlign formContentAlign; + final Function onFormDataChange; + final bool isReset; + final int updataCount; + final Function onSubmit; + const TDFormInherited({ + super.key, + required this.formData, + required Widget child, + required this.isHorizontal, + required this.isValidate, + required this.rules, + required this.formContentAlign, + required this.onFormDataChange, + required this.onSubmit, + required this.requiredMark, + required this.updataCount, + this.labelWidth, + this.formShowErrorMessage, + required this.isReset, + }) : super(child: child); + + static TDFormInherited? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + @override + bool updateShouldNotify(TDFormInherited oldWidget) { + return updataCount != oldWidget.updataCount || + isReset != oldWidget.isReset || + labelWidth != oldWidget.labelWidth || + isHorizontal != oldWidget.isHorizontal || + isValidate != oldWidget.isValidate || + rules != oldWidget.rules || + formShowErrorMessage != oldWidget.formShowErrorMessage || + formContentAlign != oldWidget.formContentAlign; + } +} diff --git a/tdesign-component/lib/src/components/form/td_form_item.dart b/tdesign-component/lib/src/components/form/td_form_item.dart new file mode 100644 index 000000000..734444ab9 --- /dev/null +++ b/tdesign-component/lib/src/components/form/td_form_item.dart @@ -0,0 +1,599 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'td_form_inherited.dart'; +import '../../../tdesign_flutter.dart'; + +/// 表格单元选用组件类型的枚举 +enum TDFormItemType { input, radios, dateTimePicker, cascader, stepper, rate, textarea, upLoadImg } + +class TDFormItem extends StatefulWidget { + TDFormItem({ + required this.type, + this.child, + this.formItemNotifier, + this.label, + this.labelWidget, + this.help, + this.name, + this.labelAlign, + this.contentAlign, + this.labelWidth, + this.tipAlign, + this.requiredMark = true, + this.formRules, + this.itemRule, + this.showErrorMessage = true, + this.indicator, + this.additionInfo, + this.select = '', + this.selectFn, + this.hintText = '', + Map? radios, + Key? key, + }) : super(key: key); + + /// 表单项标签左侧展示的内容 + final String? label; + + /// 自定义标签 + final Widget? labelWidget; + + /// 表格单元需要使用的组件类型 + final TDFormItemType type; + + /// 表单字段名称 + final String? name; + + /// TDInput的辅助信息 + final String? additionInfo; + + /// TDInput 默认显示文字 + final String? help; + + /// TODO: item 标签对齐方式 + /// 可选: left、right、top + final TextAlign? labelAlign; + + /// 表单显示内容对齐方式: + /// left、right、top + /// TODO: TDStepper TDRate 等组件没用实现通用性 + final TextAlign? contentAlign; + + /// 标签宽度,如果提供则覆盖Form的labelWidth + final double? labelWidth; + + /// 组件提示内容对齐方式 + final TextAlign? tipAlign; + + /// 表单子组件 + final Widget? child; + + final FormItemNotifier? formItemNotifier; + + /// 选择器 适用于日期选择器等 + String select; + + /// 选择器方法 适用于日期选择器等 + final Function? selectFn; + + /// 是否显示必填标记(*) + final bool? requiredMark; + + /// 整个表单的校验规则 + final List? formRules; + + /// 表单项验证规则 + final List? itemRule; + + /// 是否显示错误信息 + final bool showErrorMessage; + + /// TDTextarea 的属性,指示器 + final bool? indicator; + + ///提示内容 + final hintText; + @override + _TDFormItemState createState() => _TDFormItemState(); +} + +class _TDFormItemState extends State { + @override + void initState() { + // TODO: implement initState + super.initState(); + if(!(widget.formItemNotifier?.isDisposed ?? true)) { + widget.formItemNotifier?.addListener(() { + updateFormData(widget.formItemNotifier?.formVal); + }); + } + } + + @override + void dispose() { + super.dispose(); + if (widget.formItemNotifier != null && !widget.formItemNotifier!.isDisposed) { + widget.formItemNotifier?.dispose(); + } + } + + @override + void didChangeDependencies() { + if (FormValidate) { + startValidation(); + } + if (FormIsReset) { + errorMessage = ''; + } + super.didChangeDependencies(); + } + + /// 从 TDForm 继承获取整个表单的参数 + /// 获取真正的 LabelWidth + double get LabelWidth { + final inherited = TDFormInherited.of(context); + final defaultLabelWidth = 8.0; + + /// 如果 item 传入定制的 labelWidth 则使用 + if (widget.labelWidth != null) { + return widget.labelWidth as double; + } + + /// 使用 form 整体传入的 labelWidth + if (inherited?.labelWidth != null) { + return inherited!.labelWidth as double; + } + + return defaultLabelWidth; + } + + Map get FormData { + return TDFormInherited.of(context)!.formData; + } + + /// 获取 form 以及 formItem 的内容排列方式 + TextAlign get FormContentAlign { + final inherited = TDFormInherited.of(context); + if (widget.contentAlign != null) { + /// 断言 widget.contentAlign 不会为空 + return widget.contentAlign!; + } + + /// 如果 没用为 item 定制内容排列方式 则全部使用总表单的内容排列方式 + return inherited!.formContentAlign; + } + + /// 获取 form 是否为水平排列的状态 + bool get FormIsHorizontal { + final inherited = TDFormInherited.of(context); + if (inherited?.isHorizontal != null) { + return inherited!.isHorizontal; + } + return false; + } + + bool get FormIsReset { + final inherited = TDFormInherited.of(context); + if (inherited?.isReset != null) { + return inherited!.isReset; + } + return false; + } + + /// 获取 form 整体是否校验的信号状态 + bool get FormValidate { + final inherited = TDFormInherited.of(context); + return inherited!.isValidate; + } + + bool get FormRequiredMark { + return TDFormInherited.of(context)!.requiredMark ?? false; + } + + /// 获取整个表格是否需要展示错误提示 + bool? get ShowErrorMessage { + final inherited = TDFormInherited.of(context); + if (widget.showErrorMessage != null) { + return widget.showErrorMessage; + } else { + return inherited!.formShowErrorMessage; + } + } + + /// 获取整个表单的校验规则 + Map get FormRules { + final inherited = TDFormInherited.of(context); + return inherited!.rules; + } + + bool browseOn = false; + + /// 表单 item 的校验错误提示信息 + String? errorMessage; + + /// 调用校验方法 + void startValidation() { + setState(() { + errorMessage = validate(); + }); + } + + /// 遍历校验规则并执行 + String? validate() { + String? value = widget.formItemNotifier?.formVal; + String name = widget.name!; + if (name == null) { + return null; + } + if (FormRules[name] != null) { + TDFormValidation rule = FormRules[name]!; + + /// 只对类型匹配的项进行校验 + if (rule.type == widget.type) { + final result = rule.check(value); + if (result != null) { + /// 返回第一个不通过的错误信息 + return result; + } + } + } + + return null; + } + + void updateFormData(value) { + if (widget.name != null) { + String name = widget.name!; + Map _formData = FormData; + _formData[name] = value; + TDFormInherited.of(context)!.onFormDataChange(_formData); + startValidation(); + } + } + + @override + Widget build(BuildContext context) { + final theme = TDTheme.of(context); + Widget labelContent = Visibility( + visible: widget.label != null ? true : false, + child: SizedBox( + width: LabelWidth, + child: widget.labelWidget ?? + Row( + children: [ + TDText(widget.label, font: TDTheme.of(context).fontBodyMedium, textAlign: widget.labelAlign), + if (FormRequiredMark && (widget.requiredMark != null && widget.requiredMark == true)) + Padding( + padding: const EdgeInsets.only(left: 4), + child: TDText('*', + style: const TextStyle(fontSize: 12), textColor: Colors.red, textAlign: widget.labelAlign), + ), + ], + ))); + List itemRowContent = [ + labelContent, + Visibility( + visible: FormIsHorizontal, + child: Expanded( + child: Align( + alignment: Alignment.centerRight, + child: widget.child ?? SizedBox(), + )), + replacement: widget.child ?? SizedBox(), + ) + ]; + List itemColumnContent = [ + labelContent, + SizedBox(height: 8), + Visibility( + visible: FormIsHorizontal, + child: Expanded( + child: Align( + alignment: Alignment.centerRight, + child: widget.child ?? SizedBox(), + )), + replacement: widget.child ?? SizedBox(), + ), + ]; + switch (widget.type) { + case TDFormItemType.input: + case TDFormItemType.rate: + return Container( + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Visibility( + visible: FormIsHorizontal, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: itemRowContent, + ), + replacement: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: itemColumnContent, + ), + ), + _buildTipRow(left: 0, top: TDFormItemType.rate == widget.type ? 4 : 0) + ], + ))); + case TDFormItemType.radios: + return Container( + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Visibility( + visible: FormIsHorizontal, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: itemRowContent, + ), + replacement: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: itemColumnContent, + ), + ), + _buildTipRow(left: 0, top: 4) + ], + ))); + case TDFormItemType.dateTimePicker: + case TDFormItemType.cascader: + return _buildSelectRow(context); + case TDFormItemType.stepper: + return Container( + decoration: BoxDecoration( + color: theme.whiteColor1, + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Visibility( + visible: FormIsHorizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [labelContent, widget.child ?? SizedBox()]), + replacement: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: itemColumnContent, + ), + ), + _buildTipRow(top: 4, left: 0, right: 20) + ]), + ), + ); + case TDFormItemType.textarea: + return Container( + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Visibility( + visible: FormIsHorizontal, + child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: [ + Visibility(visible: widget.label != null ? true : false, child: labelContent), + Expanded(child: widget.child ?? SizedBox()), + ]), + replacement: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Visibility( + visible: widget.label != null ? true : false, + child: SizedBox( + width: LabelWidth, + child: widget.labelWidget ?? + TDText(widget.label, + font: TDTheme.of(context).fontBodyMedium, textAlign: widget.labelAlign), + ), + ), + widget.child ?? SizedBox() + ], + ), + ), + _buildTipRow(left: 0, top: 8) + ], + ))); + case TDFormItemType.upLoadImg: + return Container( + decoration: BoxDecoration(color: theme.whiteColor1), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + Visibility( + visible: FormIsHorizontal, + child: Row(crossAxisAlignment: CrossAxisAlignment.start, children: itemRowContent), + replacement: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Visibility( + visible: widget.label != null ? true : false, + child: SizedBox( + width: LabelWidth, + child: widget.labelWidget ?? + TDText(widget.label, + font: TDTheme.of(context).fontBodyMedium, textAlign: widget.labelAlign), + )), + widget.child ?? SizedBox() + ], + ), + ), + _buildTipRow(top: 0) + ], + )), + ); + } + } + + Widget _buildSelectRow(BuildContext context) { + Widget labelContent = SizedBox( + width: LabelWidth, + child: widget.labelWidget ?? + Padding( + padding: EdgeInsets.only(left: 2), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + TDText(widget.label ?? '', font: TDTheme.of(context).fontBodyMedium, textAlign: widget.labelAlign), + if (FormRequiredMark && (widget.requiredMark != null && widget.requiredMark == true)) + Padding( + padding: const EdgeInsets.only(left: 4), + child: TDText('*', + style: const TextStyle(fontSize: 12), textColor: Colors.red, textAlign: widget.labelAlign), + ), + ], + ), + ), + ); + Widget selectText = TDText( + textAlign: FormContentAlign, + widget.select != '' ? widget.select : widget.hintText, + font: TDTheme.of(context).fontBodyLarge, + textColor: + widget.select != '' ? TDTheme.of(context).fontGyColor1 : TDTheme.of(context).fontGyColor3.withOpacity(0.4), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ); + Widget rowContent = Padding( + padding: const EdgeInsets.only(top: 2), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded(child: selectText), + Padding( + padding: const EdgeInsets.only(left: 2), + child: Icon( + TDIcons.chevron_right, + color: TDTheme.of(context).fontGyColor3.withOpacity(0.4), + ), + ), + ], + ), + ); + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (widget.selectFn != null) { + widget.selectFn!(context); + } + }, + child: Container( + color: TDTheme.of(context).whiteColor1, + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Visibility( + visible: FormIsHorizontal, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + labelContent, + Expanded( + child: rowContent, + ) + ], + ), + replacement: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + labelContent, + Padding( + padding: const EdgeInsets.only(top: 8, left: 2), + child: selectText, + ), + ], + )), + Padding( + padding: const EdgeInsets.only(left: 2), + child: Icon( + TDIcons.chevron_right, + color: TDTheme.of(context).fontGyColor3.withOpacity(0.4), + ), + ), + ], + ), + ), + _buildTipRow(right: 28, top: 4) + ], + ), + ), + ); + } + + ///文案提示 如帮助信息,错误信息 + Widget _buildTipRow({double top = 6, double left = 4, double right = 20}) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (widget.help != null && (errorMessage == null || errorMessage == '')) + Row( + children: [ + if (widget.label != null && FormIsHorizontal) SizedBox(width: LabelWidth), + Expanded( + child: Padding( + padding: EdgeInsets.only(left: left, right: right, top: top), + child: TDText( + widget.help, + font: TDTheme.of(context).fontBodySmall, + textAlign: widget.tipAlign ?? TextAlign.left, + textColor: const Color.fromRGBO(0, 0, 0, 0.4), + )), + ) + ], + ), + if (ShowErrorMessage != null && ShowErrorMessage! && errorMessage != null && errorMessage != '') + Row( + children: [ + if (widget.label != null && FormIsHorizontal) SizedBox(width: LabelWidth), + Expanded( + child: Padding( + padding: EdgeInsets.only(left: left, right: right, top: top), + child: TDText( + errorMessage, + font: TDTheme.of(context).fontBodySmall, + textAlign: widget.tipAlign ?? TextAlign.left, + textColor: Color.fromRGBO(213, 73, 65, 1), + ))) + ], + ), + ], + ); + } +} + +class FormItemNotifier with ChangeNotifier { + + bool isDisposed = false; + String _formVal = ''; + String get formVal => _formVal; + upDataForm(val) { + _formVal = val; + notifyListeners(); + } + + @override + void dispose() { + super.dispose(); + isDisposed = true; + } +} diff --git a/tdesign-component/lib/src/components/form/td_form_validation.dart b/tdesign-component/lib/src/components/form/td_form_validation.dart new file mode 100644 index 000000000..4be7b8f92 --- /dev/null +++ b/tdesign-component/lib/src/components/form/td_form_validation.dart @@ -0,0 +1,31 @@ +import '../../../tdesign_flutter.dart'; + +/// 实现普通表单项的校验 +/// 校验规则 和 错误提醒 +class TDFormValidation { + + TDFormValidation({ + required this.validate, + required this.errorMessage, + required this.type, + }); + + /// 校验方法 + final String? Function(dynamic) validate; + + /// 错误提示信息 + final String errorMessage; + + /// 校验对象的类型 + final TDFormItemType type; + + /// 执行校验逻辑 + String? check(String? value) { + if (validate(value) != null) { + return errorMessage; + } + + /// 校验通过时返回 null + return null; + } +} diff --git a/tdesign-component/lib/src/components/icon/td_icons.dart b/tdesign-component/lib/src/components/icon/td_icons.dart new file mode 100644 index 000000000..bfaa6c43c --- /dev/null +++ b/tdesign-component/lib/src/components/icon/td_icons.dart @@ -0,0 +1,4255 @@ +import 'package:flutter/widgets.dart'; + +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: constant_identifier_names +@immutable +class _TDIconsData extends IconData { + const _TDIconsData(int codePoint, this.name) + : super( + codePoint, + fontFamily: 'TDIcons', + fontPackage: 'tdesign_flutter', + ); + + final String name; +} + + +class TDIcons { + + /// 私有构造方法,不支持外部创建,仅提供静态常量给外部使用 + const TDIcons._(); + static const accessibility_filled = _TDIconsData(0xE001, 'accessibility_filled'); + static const accessibility = _TDIconsData(0xE002, 'accessibility'); + static const activity_filled = _TDIconsData(0xE003, 'activity_filled'); + static const activity = _TDIconsData(0xE004, 'activity'); + static const add_and_subtract = _TDIconsData(0xE005, 'add_and_subtract'); + static const add_circle_filled = _TDIconsData(0xE006, 'add_circle_filled'); + static const add_circle = _TDIconsData(0xE007, 'add_circle'); + static const add_rectangle_filled = _TDIconsData(0xE008, 'add_rectangle_filled'); + static const add_rectangle = _TDIconsData(0xE009, 'add_rectangle'); + static const add = _TDIconsData(0xE00A, 'add'); + static const address_book_filled = _TDIconsData(0xE00B, 'address_book_filled'); + static const address_book = _TDIconsData(0xE00C, 'address_book'); + static const adjustment_filled = _TDIconsData(0xE00D, 'adjustment_filled'); + static const adjustment = _TDIconsData(0xE00E, 'adjustment'); + static const airplay_wave_filled = _TDIconsData(0xE00F, 'airplay_wave_filled'); + static const airplay_wave = _TDIconsData(0xE010, 'airplay_wave'); + static const alarm_add_filled = _TDIconsData(0xE011, 'alarm_add_filled'); + static const alarm_add = _TDIconsData(0xE012, 'alarm_add'); + static const alarm_filled = _TDIconsData(0xE013, 'alarm_filled'); + static const alarm_off_filled = _TDIconsData(0xE014, 'alarm_off_filled'); + static const alarm_off = _TDIconsData(0xE015, 'alarm_off'); + static const alarm = _TDIconsData(0xE016, 'alarm'); + static const align_top = _TDIconsData(0xE017, 'align_top'); + static const align_vertical = _TDIconsData(0xE018, 'align_vertical'); + static const alpha = _TDIconsData(0xE019, 'alpha'); + static const analytics_filled = _TDIconsData(0xE01A, 'analytics_filled'); + static const analytics = _TDIconsData(0xE01B, 'analytics'); + static const anchor = _TDIconsData(0xE01C, 'anchor'); + static const angry_filled = _TDIconsData(0xE01D, 'angry_filled'); + static const angry = _TDIconsData(0xE01E, 'angry'); + static const animation_1_filled = _TDIconsData(0xE01F, 'animation_1_filled'); + static const animation_1 = _TDIconsData(0xE020, 'animation_1'); + static const animation_filled = _TDIconsData(0xE021, 'animation_filled'); + static const animation = _TDIconsData(0xE022, 'animation'); + static const anticlockwise_filled = _TDIconsData(0xE023, 'anticlockwise_filled'); + static const anticlockwise = _TDIconsData(0xE024, 'anticlockwise'); + static const api = _TDIconsData(0xE025, 'api'); + static const app_filled = _TDIconsData(0xE026, 'app_filled'); + static const app = _TDIconsData(0xE027, 'app'); + static const apple_filled = _TDIconsData(0xE028, 'apple_filled'); + static const apple = _TDIconsData(0xE029, 'apple'); + static const application_filled = _TDIconsData(0xE02A, 'application_filled'); + static const application = _TDIconsData(0xE02B, 'application'); + static const architecture_hui_style_filled = _TDIconsData(0xE02C, 'architecture_hui_style_filled'); + static const architecture_hui_style = _TDIconsData(0xE02D, 'architecture_hui_style'); + static const archway_1_filled = _TDIconsData(0xE02E, 'archway_1_filled'); + static const archway_1 = _TDIconsData(0xE02F, 'archway_1'); + static const archway_filled = _TDIconsData(0xE030, 'archway_filled'); + static const archway = _TDIconsData(0xE031, 'archway'); + static const arrow_down_circle_filled = _TDIconsData(0xE032, 'arrow_down_circle_filled'); + static const arrow_down_circle = _TDIconsData(0xE033, 'arrow_down_circle'); + static const arrow_down_rectangle_filled = _TDIconsData(0xE034, 'arrow_down_rectangle_filled'); + static const arrow_down_rectangle = _TDIconsData(0xE035, 'arrow_down_rectangle'); + static const arrow_down = _TDIconsData(0xE036, 'arrow_down'); + static const arrow_left_circle_filled = _TDIconsData(0xE037, 'arrow_left_circle_filled'); + static const arrow_left_circle = _TDIconsData(0xE038, 'arrow_left_circle'); + static const arrow_left_down_circle_filled = _TDIconsData(0xE039, 'arrow_left_down_circle_filled'); + static const arrow_left_down_circle = _TDIconsData(0xE03A, 'arrow_left_down_circle'); + static const arrow_left_down = _TDIconsData(0xE03B, 'arrow_left_down'); + static const arrow_left_right_1 = _TDIconsData(0xE03C, 'arrow_left_right_1'); + static const arrow_left_right_2 = _TDIconsData(0xE03D, 'arrow_left_right_2'); + static const arrow_left_right_3 = _TDIconsData(0xE03E, 'arrow_left_right_3'); + static const arrow_left_right_circle_filled = _TDIconsData(0xE03F, 'arrow_left_right_circle_filled'); + static const arrow_left_right_circle = _TDIconsData(0xE040, 'arrow_left_right_circle'); + static const arrow_left_up_circle_filled = _TDIconsData(0xE041, 'arrow_left_up_circle_filled'); + static const arrow_left_up_circle = _TDIconsData(0xE042, 'arrow_left_up_circle'); + static const arrow_left_up = _TDIconsData(0xE043, 'arrow_left_up'); + static const arrow_left = _TDIconsData(0xE044, 'arrow_left'); + static const arrow_right_circle_filled = _TDIconsData(0xE045, 'arrow_right_circle_filled'); + static const arrow_right_circle = _TDIconsData(0xE046, 'arrow_right_circle'); + static const arrow_right_down_circle_filled = _TDIconsData(0xE047, 'arrow_right_down_circle_filled'); + static const arrow_right_down_circle = _TDIconsData(0xE048, 'arrow_right_down_circle'); + static const arrow_right_down = _TDIconsData(0xE049, 'arrow_right_down'); + static const arrow_right_up_circle_filled = _TDIconsData(0xE04A, 'arrow_right_up_circle_filled'); + static const arrow_right_up_circle = _TDIconsData(0xE04B, 'arrow_right_up_circle'); + static const arrow_right_up = _TDIconsData(0xE04C, 'arrow_right_up'); + static const arrow_right = _TDIconsData(0xE04D, 'arrow_right'); + static const arrow_triangle_down_filled = _TDIconsData(0xE04E, 'arrow_triangle_down_filled'); + static const arrow_triangle_down = _TDIconsData(0xE04F, 'arrow_triangle_down'); + static const arrow_triangle_up_filled = _TDIconsData(0xE050, 'arrow_triangle_up_filled'); + static const arrow_triangle_up = _TDIconsData(0xE051, 'arrow_triangle_up'); + static const arrow_up_circle_filled = _TDIconsData(0xE052, 'arrow_up_circle_filled'); + static const arrow_up_circle = _TDIconsData(0xE053, 'arrow_up_circle'); + static const arrow_up_down_1 = _TDIconsData(0xE054, 'arrow_up_down_1'); + static const arrow_up_down_2 = _TDIconsData(0xE055, 'arrow_up_down_2'); + static const arrow_up_down_3 = _TDIconsData(0xE056, 'arrow_up_down_3'); + static const arrow_up_down_circle_filled = _TDIconsData(0xE057, 'arrow_up_down_circle_filled'); + static const arrow_up_down_circle = _TDIconsData(0xE058, 'arrow_up_down_circle'); + static const arrow_up = _TDIconsData(0xE059, 'arrow_up'); + static const artboard = _TDIconsData(0xE05A, 'artboard'); + static const article_filled = _TDIconsData(0xE05B, 'article_filled'); + static const article = _TDIconsData(0xE05C, 'article'); + static const assignment_checked_filled = _TDIconsData(0xE05D, 'assignment_checked_filled'); + static const assignment_checked = _TDIconsData(0xE05E, 'assignment_checked'); + static const assignment_code_filled = _TDIconsData(0xE05F, 'assignment_code_filled'); + static const assignment_code = _TDIconsData(0xE060, 'assignment_code'); + static const assignment_error_filled = _TDIconsData(0xE061, 'assignment_error_filled'); + static const assignment_error = _TDIconsData(0xE062, 'assignment_error'); + static const assignment_filled = _TDIconsData(0xE063, 'assignment_filled'); + static const assignment_user_filled = _TDIconsData(0xE064, 'assignment_user_filled'); + static const assignment_user = _TDIconsData(0xE065, 'assignment_user'); + static const assignment = _TDIconsData(0xE066, 'assignment'); + static const attach = _TDIconsData(0xE067, 'attach'); + static const attic_1_filled = _TDIconsData(0xE068, 'attic_1_filled'); + static const attic_1 = _TDIconsData(0xE069, 'attic_1'); + static const attic_filled = _TDIconsData(0xE06A, 'attic_filled'); + static const attic = _TDIconsData(0xE06B, 'attic'); + static const audio_filled = _TDIconsData(0xE06C, 'audio_filled'); + static const audio = _TDIconsData(0xE06D, 'audio'); + static const awkward_filled = _TDIconsData(0xE06E, 'awkward_filled'); + static const awkward = _TDIconsData(0xE06F, 'awkward'); + static const backtop_rectangle_filled = _TDIconsData(0xE070, 'backtop_rectangle_filled'); + static const backtop_rectangle = _TDIconsData(0xE071, 'backtop_rectangle'); + static const backtop = _TDIconsData(0xE072, 'backtop'); + static const backup_filled = _TDIconsData(0xE073, 'backup_filled'); + static const backup = _TDIconsData(0xE074, 'backup'); + static const backward_filled = _TDIconsData(0xE075, 'backward_filled'); + static const backward = _TDIconsData(0xE076, 'backward'); + static const bad_laugh_filled = _TDIconsData(0xE077, 'bad_laugh_filled'); + static const bad_laugh = _TDIconsData(0xE078, 'bad_laugh'); + static const bamboo_shoot_filled = _TDIconsData(0xE079, 'bamboo_shoot_filled'); + static const bamboo_shoot = _TDIconsData(0xE07A, 'bamboo_shoot'); + static const banana_filled = _TDIconsData(0xE07B, 'banana_filled'); + static const banana = _TDIconsData(0xE07C, 'banana'); + static const barbecue_filled = _TDIconsData(0xE07D, 'barbecue_filled'); + static const barbecue = _TDIconsData(0xE07E, 'barbecue'); + static const barcode_1 = _TDIconsData(0xE07F, 'barcode_1'); + static const barcode = _TDIconsData(0xE080, 'barcode'); + static const base_station = _TDIconsData(0xE081, 'base_station'); + static const battery_add_filled = _TDIconsData(0xE082, 'battery_add_filled'); + static const battery_add = _TDIconsData(0xE083, 'battery_add'); + static const battery_charging_filled = _TDIconsData(0xE084, 'battery_charging_filled'); + static const battery_charging = _TDIconsData(0xE085, 'battery_charging'); + static const battery_filled = _TDIconsData(0xE086, 'battery_filled'); + static const battery_low_filled = _TDIconsData(0xE087, 'battery_low_filled'); + static const battery_low = _TDIconsData(0xE088, 'battery_low'); + static const battery = _TDIconsData(0xE089, 'battery'); + static const bean_filled = _TDIconsData(0xE08A, 'bean_filled'); + static const bean = _TDIconsData(0xE08B, 'bean'); + static const beer_filled = _TDIconsData(0xE08C, 'beer_filled'); + static const beer = _TDIconsData(0xE08D, 'beer'); + static const beta = _TDIconsData(0xE08E, 'beta'); + static const bifurcate_filled = _TDIconsData(0xE08F, 'bifurcate_filled'); + static const bifurcate = _TDIconsData(0xE090, 'bifurcate'); + static const bill_filled = _TDIconsData(0xE091, 'bill_filled'); + static const bill = _TDIconsData(0xE092, 'bill'); + static const bluetooth = _TDIconsData(0xE093, 'bluetooth'); + static const bone_filled = _TDIconsData(0xE094, 'bone_filled'); + static const bone = _TDIconsData(0xE095, 'bone'); + static const book_filled = _TDIconsData(0xE096, 'book_filled'); + static const book_open_filled = _TDIconsData(0xE097, 'book_open_filled'); + static const book_open = _TDIconsData(0xE098, 'book_open'); + static const book_unknown_filled = _TDIconsData(0xE099, 'book_unknown_filled'); + static const book_unknown = _TDIconsData(0xE09A, 'book_unknown'); + static const book = _TDIconsData(0xE09B, 'book'); + static const bookmark_add_filled = _TDIconsData(0xE09C, 'bookmark_add_filled'); + static const bookmark_add = _TDIconsData(0xE09D, 'bookmark_add'); + static const bookmark_checked_filled = _TDIconsData(0xE09E, 'bookmark_checked_filled'); + static const bookmark_checked = _TDIconsData(0xE09F, 'bookmark_checked'); + static const bookmark_double_filled = _TDIconsData(0xE0A0, 'bookmark_double_filled'); + static const bookmark_double = _TDIconsData(0xE0A1, 'bookmark_double'); + static const bookmark_filled = _TDIconsData(0xE0A2, 'bookmark_filled'); + static const bookmark_minus_filled = _TDIconsData(0xE0A3, 'bookmark_minus_filled'); + static const bookmark_minus = _TDIconsData(0xE0A4, 'bookmark_minus'); + static const bookmark = _TDIconsData(0xE0A5, 'bookmark'); + static const braces = _TDIconsData(0xE0A6, 'braces'); + static const brackets = _TDIconsData(0xE0A7, 'brackets'); + static const bread_filled = _TDIconsData(0xE0A8, 'bread_filled'); + static const bread = _TDIconsData(0xE0A9, 'bread'); + static const bridge_1_filled = _TDIconsData(0xE0AA, 'bridge_1_filled'); + static const bridge_1 = _TDIconsData(0xE0AB, 'bridge_1'); + static const bridge_2_filled = _TDIconsData(0xE0AC, 'bridge_2_filled'); + static const bridge_2 = _TDIconsData(0xE0AD, 'bridge_2'); + static const bridge_3 = _TDIconsData(0xE0AE, 'bridge_3'); + static const bridge_4 = _TDIconsData(0xE0AF, 'bridge_4'); + static const bridge_5_filled = _TDIconsData(0xE0B0, 'bridge_5_filled'); + static const bridge_5 = _TDIconsData(0xE0B1, 'bridge_5'); + static const bridge_6_filled = _TDIconsData(0xE0B2, 'bridge_6_filled'); + static const bridge_6 = _TDIconsData(0xE0B3, 'bridge_6'); + static const bridge = _TDIconsData(0xE0B4, 'bridge'); + static const brightness_1_filled = _TDIconsData(0xE0B5, 'brightness_1_filled'); + static const brightness_1 = _TDIconsData(0xE0B6, 'brightness_1'); + static const brightness_filled = _TDIconsData(0xE0B7, 'brightness_filled'); + static const brightness = _TDIconsData(0xE0B8, 'brightness'); + static const broccoli_filled = _TDIconsData(0xE0B9, 'broccoli_filled'); + static const broccoli = _TDIconsData(0xE0BA, 'broccoli'); + static const browse_filled = _TDIconsData(0xE0BB, 'browse_filled'); + static const browse_gallery_filled = _TDIconsData(0xE0BC, 'browse_gallery_filled'); + static const browse_gallery = _TDIconsData(0xE0BD, 'browse_gallery'); + static const browse_off_filled = _TDIconsData(0xE0BE, 'browse_off_filled'); + static const browse_off = _TDIconsData(0xE0BF, 'browse_off'); + static const browse = _TDIconsData(0xE0C0, 'browse'); + static const brush_filled = _TDIconsData(0xE0C1, 'brush_filled'); + static const brush = _TDIconsData(0xE0C2, 'brush'); + static const bug_filled = _TDIconsData(0xE0C3, 'bug_filled'); + static const bug_report_filled = _TDIconsData(0xE0C4, 'bug_report_filled'); + static const bug_report = _TDIconsData(0xE0C5, 'bug_report'); + static const bug = _TDIconsData(0xE0C6, 'bug'); + static const building_1_filled = _TDIconsData(0xE0C7, 'building_1_filled'); + static const building_1 = _TDIconsData(0xE0C8, 'building_1'); + static const building_2_filled = _TDIconsData(0xE0C9, 'building_2_filled'); + static const building_2 = _TDIconsData(0xE0CA, 'building_2'); + static const building_3_filled = _TDIconsData(0xE0CB, 'building_3_filled'); + static const building_3 = _TDIconsData(0xE0CC, 'building_3'); + static const building_4_filled = _TDIconsData(0xE0CD, 'building_4_filled'); + static const building_4 = _TDIconsData(0xE0CE, 'building_4'); + static const building_5_filled = _TDIconsData(0xE0CF, 'building_5_filled'); + static const building_5 = _TDIconsData(0xE0D0, 'building_5'); + static const building_filled = _TDIconsData(0xE0D1, 'building_filled'); + static const building = _TDIconsData(0xE0D2, 'building'); + static const bulletpoint = _TDIconsData(0xE0D3, 'bulletpoint'); + static const button_filled = _TDIconsData(0xE0D4, 'button_filled'); + static const button = _TDIconsData(0xE0D5, 'button'); + static const cabbage_filled = _TDIconsData(0xE0D6, 'cabbage_filled'); + static const cabbage = _TDIconsData(0xE0D7, 'cabbage'); + static const cake_filled = _TDIconsData(0xE0D8, 'cake_filled'); + static const cake = _TDIconsData(0xE0D9, 'cake'); + static const calculation_1_filled = _TDIconsData(0xE0DA, 'calculation_1_filled'); + static const calculation_1 = _TDIconsData(0xE0DB, 'calculation_1'); + static const calculation = _TDIconsData(0xE0DC, 'calculation'); + static const calculator_1 = _TDIconsData(0xE0DD, 'calculator_1'); + static const calculator_filled = _TDIconsData(0xE0DE, 'calculator_filled'); + static const calculator = _TDIconsData(0xE0DF, 'calculator'); + static const calendar_1_filled = _TDIconsData(0xE0E0, 'calendar_1_filled'); + static const calendar_1 = _TDIconsData(0xE0E1, 'calendar_1'); + static const calendar_2_filled = _TDIconsData(0xE0E2, 'calendar_2_filled'); + static const calendar_2 = _TDIconsData(0xE0E3, 'calendar_2'); + static const calendar_edit_filled = _TDIconsData(0xE0E4, 'calendar_edit_filled'); + static const calendar_edit = _TDIconsData(0xE0E5, 'calendar_edit'); + static const calendar_event_filled = _TDIconsData(0xE0E6, 'calendar_event_filled'); + static const calendar_event = _TDIconsData(0xE0E7, 'calendar_event'); + static const calendar_filled = _TDIconsData(0xE0E8, 'calendar_filled'); + static const calendar = _TDIconsData(0xE0E9, 'calendar'); + static const call_1_filled = _TDIconsData(0xE0EA, 'call_1_filled'); + static const call_1 = _TDIconsData(0xE0EB, 'call_1'); + static const call_cancel_filled = _TDIconsData(0xE0EC, 'call_cancel_filled'); + static const call_cancel = _TDIconsData(0xE0ED, 'call_cancel'); + static const call_filled = _TDIconsData(0xE0EE, 'call_filled'); + static const call_forwarded_filled = _TDIconsData(0xE0EF, 'call_forwarded_filled'); + static const call_forwarded = _TDIconsData(0xE0F0, 'call_forwarded'); + static const call_incoming_filled = _TDIconsData(0xE0F1, 'call_incoming_filled'); + static const call_incoming = _TDIconsData(0xE0F2, 'call_incoming'); + static const call_off_filled = _TDIconsData(0xE0F3, 'call_off_filled'); + static const call_off = _TDIconsData(0xE0F4, 'call_off'); + static const call = _TDIconsData(0xE0F5, 'call'); + static const calm_1_filled = _TDIconsData(0xE0F6, 'calm_1_filled'); + static const calm_1 = _TDIconsData(0xE0F7, 'calm_1'); + static const calm_filled = _TDIconsData(0xE0F8, 'calm_filled'); + static const calm = _TDIconsData(0xE0F9, 'calm'); + static const camera_1_filled = _TDIconsData(0xE0FA, 'camera_1_filled'); + static const camera_1 = _TDIconsData(0xE0FB, 'camera_1'); + static const camera_2_filled = _TDIconsData(0xE0FC, 'camera_2_filled'); + static const camera_2 = _TDIconsData(0xE0FD, 'camera_2'); + static const camera_filled = _TDIconsData(0xE0FE, 'camera_filled'); + static const camera_off_filled = _TDIconsData(0xE0FF, 'camera_off_filled'); + static const camera_off = _TDIconsData(0xE100, 'camera_off'); + static const camera = _TDIconsData(0xE101, 'camera'); + static const candy_filled = _TDIconsData(0xE102, 'candy_filled'); + static const candy = _TDIconsData(0xE103, 'candy'); + static const card_filled = _TDIconsData(0xE104, 'card_filled'); + static const card = _TDIconsData(0xE105, 'card'); + static const cardmembership_filled = _TDIconsData(0xE106, 'cardmembership_filled'); + static const cardmembership = _TDIconsData(0xE107, 'cardmembership'); + static const caret_down_small = _TDIconsData(0xE108, 'caret_down_small'); + static const caret_down = _TDIconsData(0xE109, 'caret_down'); + static const caret_left_small = _TDIconsData(0xE10A, 'caret_left_small'); + static const caret_left = _TDIconsData(0xE10B, 'caret_left'); + static const caret_right_small = _TDIconsData(0xE10C, 'caret_right_small'); + static const caret_right = _TDIconsData(0xE10D, 'caret_right'); + static const caret_up_small = _TDIconsData(0xE10E, 'caret_up_small'); + static const caret_up = _TDIconsData(0xE10F, 'caret_up'); + static const cart_add_filled = _TDIconsData(0xE110, 'cart_add_filled'); + static const cart_add = _TDIconsData(0xE111, 'cart_add'); + static const cart_filled = _TDIconsData(0xE112, 'cart_filled'); + static const cart = _TDIconsData(0xE113, 'cart'); + static const cast_filled = _TDIconsData(0xE114, 'cast_filled'); + static const cast = _TDIconsData(0xE115, 'cast'); + static const castle_1_filled = _TDIconsData(0xE116, 'castle_1_filled'); + static const castle_1 = _TDIconsData(0xE117, 'castle_1'); + static const castle_2_filled = _TDIconsData(0xE118, 'castle_2_filled'); + static const castle_2 = _TDIconsData(0xE119, 'castle_2'); + static const castle_3_filled = _TDIconsData(0xE11A, 'castle_3_filled'); + static const castle_3 = _TDIconsData(0xE11B, 'castle_3'); + static const castle_4_filled = _TDIconsData(0xE11C, 'castle_4_filled'); + static const castle_4 = _TDIconsData(0xE11D, 'castle_4'); + static const castle_5_filled = _TDIconsData(0xE11E, 'castle_5_filled'); + static const castle_5 = _TDIconsData(0xE11F, 'castle_5'); + static const castle_6_filled = _TDIconsData(0xE120, 'castle_6_filled'); + static const castle_6 = _TDIconsData(0xE121, 'castle_6'); + static const castle_7_filled = _TDIconsData(0xE122, 'castle_7_filled'); + static const castle_7 = _TDIconsData(0xE123, 'castle_7'); + static const castle_filled = _TDIconsData(0xE124, 'castle_filled'); + static const castle = _TDIconsData(0xE125, 'castle'); + static const cat_filled = _TDIconsData(0xE126, 'cat_filled'); + static const cat = _TDIconsData(0xE127, 'cat'); + static const catalog_filled = _TDIconsData(0xE128, 'catalog_filled'); + static const catalog = _TDIconsData(0xE129, 'catalog'); + static const cd_filled = _TDIconsData(0xE12A, 'cd_filled'); + static const cd = _TDIconsData(0xE12B, 'cd'); + static const celsius = _TDIconsData(0xE12C, 'celsius'); + static const center_focus_strong_filled = _TDIconsData(0xE12D, 'center_focus_strong_filled'); + static const center_focus_strong = _TDIconsData(0xE12E, 'center_focus_strong'); + static const centimeter = _TDIconsData(0xE12F, 'centimeter'); + static const certificate_1_filled = _TDIconsData(0xE130, 'certificate_1_filled'); + static const certificate_1 = _TDIconsData(0xE131, 'certificate_1'); + static const certificate_filled = _TDIconsData(0xE132, 'certificate_filled'); + static const certificate = _TDIconsData(0xE133, 'certificate'); + static const chart_3d_filled = _TDIconsData(0xE134, 'chart_3d_filled'); + static const chart_3d = _TDIconsData(0xE135, 'chart_3d'); + static const chart_add_filled = _TDIconsData(0xE136, 'chart_add_filled'); + static const chart_add = _TDIconsData(0xE137, 'chart_add'); + static const chart_analytics = _TDIconsData(0xE138, 'chart_analytics'); + static const chart_area_filled = _TDIconsData(0xE139, 'chart_area_filled'); + static const chart_area_multi_filled = _TDIconsData(0xE13A, 'chart_area_multi_filled'); + static const chart_area_multi = _TDIconsData(0xE13B, 'chart_area_multi'); + static const chart_area = _TDIconsData(0xE13C, 'chart_area'); + static const chart_bar_filled = _TDIconsData(0xE13D, 'chart_bar_filled'); + static const chart_bar = _TDIconsData(0xE13E, 'chart_bar'); + static const chart_bubble_filled = _TDIconsData(0xE13F, 'chart_bubble_filled'); + static const chart_bubble = _TDIconsData(0xE140, 'chart_bubble'); + static const chart_column_filled = _TDIconsData(0xE141, 'chart_column_filled'); + static const chart_column = _TDIconsData(0xE142, 'chart_column'); + static const chart_combo_filled = _TDIconsData(0xE143, 'chart_combo_filled'); + static const chart_combo = _TDIconsData(0xE144, 'chart_combo'); + static const chart_filled = _TDIconsData(0xE145, 'chart_filled'); + static const chart_line_data_1 = _TDIconsData(0xE146, 'chart_line_data_1'); + static const chart_line_data = _TDIconsData(0xE147, 'chart_line_data'); + static const chart_line_multi = _TDIconsData(0xE148, 'chart_line_multi'); + static const chart_line = _TDIconsData(0xE149, 'chart_line'); + static const chart_maximum = _TDIconsData(0xE14A, 'chart_maximum'); + static const chart_median = _TDIconsData(0xE14B, 'chart_median'); + static const chart_minimum = _TDIconsData(0xE14C, 'chart_minimum'); + static const chart_pie_filled = _TDIconsData(0xE14D, 'chart_pie_filled'); + static const chart_pie = _TDIconsData(0xE14E, 'chart_pie'); + static const chart_radar_filled = _TDIconsData(0xE14F, 'chart_radar_filled'); + static const chart_radar = _TDIconsData(0xE150, 'chart_radar'); + static const chart_radial = _TDIconsData(0xE151, 'chart_radial'); + static const chart_ring_1_filled = _TDIconsData(0xE152, 'chart_ring_1_filled'); + static const chart_ring_1 = _TDIconsData(0xE153, 'chart_ring_1'); + static const chart_ring_filled = _TDIconsData(0xE154, 'chart_ring_filled'); + static const chart_ring = _TDIconsData(0xE155, 'chart_ring'); + static const chart_scatter = _TDIconsData(0xE156, 'chart_scatter'); + static const chart_stacked_filled = _TDIconsData(0xE157, 'chart_stacked_filled'); + static const chart_stacked = _TDIconsData(0xE158, 'chart_stacked'); + static const chart = _TDIconsData(0xE159, 'chart'); + static const chat_add_filled = _TDIconsData(0xE15A, 'chat_add_filled'); + static const chat_add = _TDIconsData(0xE15B, 'chat_add'); + static const chat_bubble_1_filled = _TDIconsData(0xE15C, 'chat_bubble_1_filled'); + static const chat_bubble_1 = _TDIconsData(0xE15D, 'chat_bubble_1'); + static const chat_bubble_add_filled = _TDIconsData(0xE15E, 'chat_bubble_add_filled'); + static const chat_bubble_add = _TDIconsData(0xE15F, 'chat_bubble_add'); + static const chat_bubble_error_filled = _TDIconsData(0xE160, 'chat_bubble_error_filled'); + static const chat_bubble_error = _TDIconsData(0xE161, 'chat_bubble_error'); + static const chat_bubble_filled = _TDIconsData(0xE162, 'chat_bubble_filled'); + static const chat_bubble_help_filled = _TDIconsData(0xE163, 'chat_bubble_help_filled'); + static const chat_bubble_help = _TDIconsData(0xE164, 'chat_bubble_help'); + static const chat_bubble_history_filled = _TDIconsData(0xE165, 'chat_bubble_history_filled'); + static const chat_bubble_history = _TDIconsData(0xE166, 'chat_bubble_history'); + static const chat_bubble_locked_filled = _TDIconsData(0xE167, 'chat_bubble_locked_filled'); + static const chat_bubble_locked = _TDIconsData(0xE168, 'chat_bubble_locked'); + static const chat_bubble_smile_filled = _TDIconsData(0xE169, 'chat_bubble_smile_filled'); + static const chat_bubble_smile = _TDIconsData(0xE16A, 'chat_bubble_smile'); + static const chat_bubble = _TDIconsData(0xE16B, 'chat_bubble'); + static const chat_checked_filled = _TDIconsData(0xE16C, 'chat_checked_filled'); + static const chat_checked = _TDIconsData(0xE16D, 'chat_checked'); + static const chat_clear_filled = _TDIconsData(0xE16E, 'chat_clear_filled'); + static const chat_clear = _TDIconsData(0xE16F, 'chat_clear'); + static const chat_double_filled = _TDIconsData(0xE170, 'chat_double_filled'); + static const chat_double = _TDIconsData(0xE171, 'chat_double'); + static const chat_error_filled = _TDIconsData(0xE172, 'chat_error_filled'); + static const chat_error = _TDIconsData(0xE173, 'chat_error'); + static const chat_filled = _TDIconsData(0xE174, 'chat_filled'); + static const chat_heart_filled = _TDIconsData(0xE175, 'chat_heart_filled'); + static const chat_heart = _TDIconsData(0xE176, 'chat_heart'); + static const chat_message_filled = _TDIconsData(0xE177, 'chat_message_filled'); + static const chat_message = _TDIconsData(0xE178, 'chat_message'); + static const chat_off_filled = _TDIconsData(0xE179, 'chat_off_filled'); + static const chat_off = _TDIconsData(0xE17A, 'chat_off'); + static const chat_poll_filled = _TDIconsData(0xE17B, 'chat_poll_filled'); + static const chat_poll = _TDIconsData(0xE17C, 'chat_poll'); + static const chat_setting_filled = _TDIconsData(0xE17D, 'chat_setting_filled'); + static const chat_setting = _TDIconsData(0xE17E, 'chat_setting'); + static const chat = _TDIconsData(0xE17F, 'chat'); + static const check_circle_filled = _TDIconsData(0xE180, 'check_circle_filled'); + static const check_circle = _TDIconsData(0xE181, 'check_circle'); + static const check_double = _TDIconsData(0xE182, 'check_double'); + static const check_rectangle_filled = _TDIconsData(0xE183, 'check_rectangle_filled'); + static const check_rectangle = _TDIconsData(0xE184, 'check_rectangle'); + static const check = _TDIconsData(0xE185, 'check'); + static const cheese_filled = _TDIconsData(0xE186, 'cheese_filled'); + static const cheese = _TDIconsData(0xE187, 'cheese'); + static const cherry_filled = _TDIconsData(0xE188, 'cherry_filled'); + static const cherry = _TDIconsData(0xE189, 'cherry'); + static const chevron_down_circle_filled = _TDIconsData(0xE18A, 'chevron_down_circle_filled'); + static const chevron_down_circle = _TDIconsData(0xE18B, 'chevron_down_circle'); + static const chevron_down_double_s = _TDIconsData(0xE18C, 'chevron_down_double_s'); + static const chevron_down_double = _TDIconsData(0xE18D, 'chevron_down_double'); + static const chevron_down_rectangle_filled = _TDIconsData(0xE18E, 'chevron_down_rectangle_filled'); + static const chevron_down_rectangle = _TDIconsData(0xE18F, 'chevron_down_rectangle'); + static const chevron_down_s = _TDIconsData(0xE190, 'chevron_down_s'); + static const chevron_down = _TDIconsData(0xE191, 'chevron_down'); + static const chevron_left_circle_filled = _TDIconsData(0xE192, 'chevron_left_circle_filled'); + static const chevron_left_circle = _TDIconsData(0xE193, 'chevron_left_circle'); + static const chevron_left_double_s = _TDIconsData(0xE194, 'chevron_left_double_s'); + static const chevron_left_double = _TDIconsData(0xE195, 'chevron_left_double'); + static const chevron_left_rectangle_filled = _TDIconsData(0xE196, 'chevron_left_rectangle_filled'); + static const chevron_left_rectangle = _TDIconsData(0xE197, 'chevron_left_rectangle'); + static const chevron_left_s = _TDIconsData(0xE198, 'chevron_left_s'); + static const chevron_left = _TDIconsData(0xE199, 'chevron_left'); + static const chevron_right_circle_filled = _TDIconsData(0xE19A, 'chevron_right_circle_filled'); + static const chevron_right_circle = _TDIconsData(0xE19B, 'chevron_right_circle'); + static const chevron_right_double_s = _TDIconsData(0xE19C, 'chevron_right_double_s'); + static const chevron_right_double = _TDIconsData(0xE19D, 'chevron_right_double'); + static const chevron_right_rectangle_filled = _TDIconsData(0xE19E, 'chevron_right_rectangle_filled'); + static const chevron_right_rectangle = _TDIconsData(0xE19F, 'chevron_right_rectangle'); + static const chevron_right_s = _TDIconsData(0xE1A0, 'chevron_right_s'); + static const chevron_right = _TDIconsData(0xE1A1, 'chevron_right'); + static const chevron_up_circle_filled = _TDIconsData(0xE1A2, 'chevron_up_circle_filled'); + static const chevron_up_circle = _TDIconsData(0xE1A3, 'chevron_up_circle'); + static const chevron_up_double_s = _TDIconsData(0xE1A4, 'chevron_up_double_s'); + static const chevron_up_double = _TDIconsData(0xE1A5, 'chevron_up_double'); + static const chevron_up_rectangle_filled = _TDIconsData(0xE1A6, 'chevron_up_rectangle_filled'); + static const chevron_up_rectangle = _TDIconsData(0xE1A7, 'chevron_up_rectangle'); + static const chevron_up_s = _TDIconsData(0xE1A8, 'chevron_up_s'); + static const chevron_up = _TDIconsData(0xE1A9, 'chevron_up'); + static const chicken = _TDIconsData(0xE1AA, 'chicken'); + static const chili_filled = _TDIconsData(0xE1AB, 'chili_filled'); + static const chili = _TDIconsData(0xE1AC, 'chili'); + static const chimney_1_filled = _TDIconsData(0xE1AD, 'chimney_1_filled'); + static const chimney_1 = _TDIconsData(0xE1AE, 'chimney_1'); + static const chimney_2_filled = _TDIconsData(0xE1AF, 'chimney_2_filled'); + static const chimney_2 = _TDIconsData(0xE1B0, 'chimney_2'); + static const chimney_filled = _TDIconsData(0xE1B1, 'chimney_filled'); + static const chimney = _TDIconsData(0xE1B2, 'chimney'); + static const chinese_cabbage_filled = _TDIconsData(0xE1B3, 'chinese_cabbage_filled'); + static const chinese_cabbage = _TDIconsData(0xE1B4, 'chinese_cabbage'); + static const church_filled = _TDIconsData(0xE1B5, 'church_filled'); + static const church = _TDIconsData(0xE1B6, 'church'); + static const circle_filled = _TDIconsData(0xE1B7, 'circle_filled'); + static const circle = _TDIconsData(0xE1B8, 'circle'); + static const city_1_filled = _TDIconsData(0xE1B9, 'city_1_filled'); + static const city_1 = _TDIconsData(0xE1BA, 'city_1'); + static const city_10_filled = _TDIconsData(0xE1BB, 'city_10_filled'); + static const city_10 = _TDIconsData(0xE1BC, 'city_10'); + static const city_11_filled = _TDIconsData(0xE1BD, 'city_11_filled'); + static const city_11 = _TDIconsData(0xE1BE, 'city_11'); + static const city_12_filled = _TDIconsData(0xE1BF, 'city_12_filled'); + static const city_12 = _TDIconsData(0xE1C0, 'city_12'); + static const city_13_filled = _TDIconsData(0xE1C1, 'city_13_filled'); + static const city_13 = _TDIconsData(0xE1C2, 'city_13'); + static const city_14_filled = _TDIconsData(0xE1C3, 'city_14_filled'); + static const city_14 = _TDIconsData(0xE1C4, 'city_14'); + static const city_15_filled = _TDIconsData(0xE1C5, 'city_15_filled'); + static const city_15 = _TDIconsData(0xE1C6, 'city_15'); + static const city_2_filled = _TDIconsData(0xE1C7, 'city_2_filled'); + static const city_2 = _TDIconsData(0xE1C8, 'city_2'); + static const city_3_filled = _TDIconsData(0xE1C9, 'city_3_filled'); + static const city_3 = _TDIconsData(0xE1CA, 'city_3'); + static const city_4_filled = _TDIconsData(0xE1CB, 'city_4_filled'); + static const city_4 = _TDIconsData(0xE1CC, 'city_4'); + static const city_5_filled = _TDIconsData(0xE1CD, 'city_5_filled'); + static const city_5 = _TDIconsData(0xE1CE, 'city_5'); + static const city_6_filled = _TDIconsData(0xE1CF, 'city_6_filled'); + static const city_6 = _TDIconsData(0xE1D0, 'city_6'); + static const city_7_filled = _TDIconsData(0xE1D1, 'city_7_filled'); + static const city_7 = _TDIconsData(0xE1D2, 'city_7'); + static const city_8_filled = _TDIconsData(0xE1D3, 'city_8_filled'); + static const city_8 = _TDIconsData(0xE1D4, 'city_8'); + static const city_9_filled = _TDIconsData(0xE1D5, 'city_9_filled'); + static const city_9 = _TDIconsData(0xE1D6, 'city_9'); + static const city_ancient_1_filled = _TDIconsData(0xE1D7, 'city_ancient_1_filled'); + static const city_ancient_1 = _TDIconsData(0xE1D8, 'city_ancient_1'); + static const city_ancient_2_filled = _TDIconsData(0xE1D9, 'city_ancient_2_filled'); + static const city_ancient_2 = _TDIconsData(0xE1DA, 'city_ancient_2'); + static const city_ancient_filled = _TDIconsData(0xE1DB, 'city_ancient_filled'); + static const city_ancient = _TDIconsData(0xE1DC, 'city_ancient'); + static const city_filled = _TDIconsData(0xE1DD, 'city_filled'); + static const city = _TDIconsData(0xE1DE, 'city'); + static const clear_filled = _TDIconsData(0xE1DF, 'clear_filled'); + static const clear_formatting_1_filled = _TDIconsData(0xE1E0, 'clear_formatting_1_filled'); + static const clear_formatting_1 = _TDIconsData(0xE1E1, 'clear_formatting_1'); + static const clear_formatting_filled = _TDIconsData(0xE1E2, 'clear_formatting_filled'); + static const clear_formatting = _TDIconsData(0xE1E3, 'clear_formatting'); + static const clear = _TDIconsData(0xE1E4, 'clear'); + static const close_circle_filled = _TDIconsData(0xE1E5, 'close_circle_filled'); + static const close_circle = _TDIconsData(0xE1E6, 'close_circle'); + static const close_octagon_filled = _TDIconsData(0xE1E7, 'close_octagon_filled'); + static const close_octagon = _TDIconsData(0xE1E8, 'close_octagon'); + static const close_rectangle_filled = _TDIconsData(0xE1E9, 'close_rectangle_filled'); + static const close_rectangle = _TDIconsData(0xE1EA, 'close_rectangle'); + static const close = _TDIconsData(0xE1EB, 'close'); + static const cloud_download = _TDIconsData(0xE1EC, 'cloud_download'); + static const cloud_filled = _TDIconsData(0xE1ED, 'cloud_filled'); + static const cloud_upload = _TDIconsData(0xE1EE, 'cloud_upload'); + static const cloud = _TDIconsData(0xE1EF, 'cloud'); + static const cloudy_day_filled = _TDIconsData(0xE1F0, 'cloudy_day_filled'); + static const cloudy_day = _TDIconsData(0xE1F1, 'cloudy_day'); + static const cloudy_night_filled = _TDIconsData(0xE1F2, 'cloudy_night_filled'); + static const cloudy_night_rain_filled = _TDIconsData(0xE1F3, 'cloudy_night_rain_filled'); + static const cloudy_night_rain = _TDIconsData(0xE1F4, 'cloudy_night_rain'); + static const cloudy_night = _TDIconsData(0xE1F5, 'cloudy_night'); + static const cloudy_rain_filled = _TDIconsData(0xE1F6, 'cloudy_rain_filled'); + static const cloudy_rain = _TDIconsData(0xE1F7, 'cloudy_rain'); + static const cloudy_sunny_filled = _TDIconsData(0xE1F8, 'cloudy_sunny_filled'); + static const cloudy_sunny = _TDIconsData(0xE1F9, 'cloudy_sunny'); + static const code_1 = _TDIconsData(0xE1FA, 'code_1'); + static const code_off = _TDIconsData(0xE1FB, 'code_off'); + static const code = _TDIconsData(0xE1FC, 'code'); + static const cola_filled = _TDIconsData(0xE1FD, 'cola_filled'); + static const cola = _TDIconsData(0xE1FE, 'cola'); + static const collage_filled = _TDIconsData(0xE1FF, 'collage_filled'); + static const collage = _TDIconsData(0xE200, 'collage'); + static const collection_filled = _TDIconsData(0xE201, 'collection_filled'); + static const collection = _TDIconsData(0xE202, 'collection'); + static const color_invert_filled = _TDIconsData(0xE203, 'color_invert_filled'); + static const color_invert = _TDIconsData(0xE204, 'color_invert'); + static const combination_filled = _TDIconsData(0xE205, 'combination_filled'); + static const combination = _TDIconsData(0xE206, 'combination'); + static const command = _TDIconsData(0xE207, 'command'); + static const compass_1_filled = _TDIconsData(0xE208, 'compass_1_filled'); + static const compass_1 = _TDIconsData(0xE209, 'compass_1'); + static const compass_filled = _TDIconsData(0xE20A, 'compass_filled'); + static const compass = _TDIconsData(0xE20B, 'compass'); + static const component_breadcrumb_filled = _TDIconsData(0xE20C, 'component_breadcrumb_filled'); + static const component_breadcrumb = _TDIconsData(0xE20D, 'component_breadcrumb'); + static const component_checkbox_filled = _TDIconsData(0xE20E, 'component_checkbox_filled'); + static const component_checkbox = _TDIconsData(0xE20F, 'component_checkbox'); + static const component_divider_horizontal_filled = _TDIconsData(0xE210, 'component_divider_horizontal_filled'); + static const component_divider_horizontal = _TDIconsData(0xE211, 'component_divider_horizontal'); + static const component_divider_vertical_filled = _TDIconsData(0xE212, 'component_divider_vertical_filled'); + static const component_divider_vertical = _TDIconsData(0xE213, 'component_divider_vertical'); + static const component_dropdown_filled = _TDIconsData(0xE214, 'component_dropdown_filled'); + static const component_dropdown = _TDIconsData(0xE215, 'component_dropdown'); + static const component_grid_filled = _TDIconsData(0xE216, 'component_grid_filled'); + static const component_grid = _TDIconsData(0xE217, 'component_grid'); + static const component_input_filled = _TDIconsData(0xE218, 'component_input_filled'); + static const component_input = _TDIconsData(0xE219, 'component_input'); + static const component_layout_filled = _TDIconsData(0xE21A, 'component_layout_filled'); + static const component_layout = _TDIconsData(0xE21B, 'component_layout'); + static const component_radio = _TDIconsData(0xE21C, 'component_radio'); + static const component_space_filled = _TDIconsData(0xE21D, 'component_space_filled'); + static const component_space = _TDIconsData(0xE21E, 'component_space'); + static const component_steps_filled = _TDIconsData(0xE21F, 'component_steps_filled'); + static const component_steps = _TDIconsData(0xE220, 'component_steps'); + static const component_switch_filled = _TDIconsData(0xE221, 'component_switch_filled'); + static const component_switch = _TDIconsData(0xE222, 'component_switch'); + static const constraint = _TDIconsData(0xE223, 'constraint'); + static const contrast_1_filled = _TDIconsData(0xE224, 'contrast_1_filled'); + static const contrast_1 = _TDIconsData(0xE225, 'contrast_1'); + static const contrast_filled = _TDIconsData(0xE226, 'contrast_filled'); + static const contrast = _TDIconsData(0xE227, 'contrast'); + static const control_platform_filled = _TDIconsData(0xE228, 'control_platform_filled'); + static const control_platform = _TDIconsData(0xE229, 'control_platform'); + static const cooperate_filled = _TDIconsData(0xE22A, 'cooperate_filled'); + static const cooperate = _TDIconsData(0xE22B, 'cooperate'); + static const coordinate_system_filled = _TDIconsData(0xE22C, 'coordinate_system_filled'); + static const coordinate_system = _TDIconsData(0xE22D, 'coordinate_system'); + static const copy_filled = _TDIconsData(0xE22E, 'copy_filled'); + static const copy = _TDIconsData(0xE22F, 'copy'); + static const copyright_filled = _TDIconsData(0xE230, 'copyright_filled'); + static const copyright = _TDIconsData(0xE231, 'copyright'); + static const corn_filled = _TDIconsData(0xE232, 'corn_filled'); + static const corn = _TDIconsData(0xE233, 'corn'); + static const coupon_filled = _TDIconsData(0xE234, 'coupon_filled'); + static const coupon = _TDIconsData(0xE235, 'coupon'); + static const course_filled = _TDIconsData(0xE236, 'course_filled'); + static const course = _TDIconsData(0xE237, 'course'); + static const cpu_filled = _TDIconsData(0xE238, 'cpu_filled'); + static const cpu = _TDIconsData(0xE239, 'cpu'); + static const crack_filled = _TDIconsData(0xE23A, 'crack_filled'); + static const crack = _TDIconsData(0xE23B, 'crack'); + static const creditcard_add_filled = _TDIconsData(0xE23C, 'creditcard_add_filled'); + static const creditcard_add = _TDIconsData(0xE23D, 'creditcard_add'); + static const creditcard_filled = _TDIconsData(0xE23E, 'creditcard_filled'); + static const creditcard_off_filled = _TDIconsData(0xE23F, 'creditcard_off_filled'); + static const creditcard_off = _TDIconsData(0xE240, 'creditcard_off'); + static const creditcard = _TDIconsData(0xE241, 'creditcard'); + static const crooked_smile_filled = _TDIconsData(0xE242, 'crooked_smile_filled'); + static const crooked_smile = _TDIconsData(0xE243, 'crooked_smile'); + static const cry_and_laugh_filled = _TDIconsData(0xE244, 'cry_and_laugh_filled'); + static const cry_and_laugh = _TDIconsData(0xE245, 'cry_and_laugh'); + static const cry_loudly_filled = _TDIconsData(0xE246, 'cry_loudly_filled'); + static const cry_loudly = _TDIconsData(0xE247, 'cry_loudly'); + static const css3_filled = _TDIconsData(0xE248, 'css3_filled'); + static const css3 = _TDIconsData(0xE249, 'css3'); + static const cucumber = _TDIconsData(0xE24A, 'cucumber'); + static const currency_exchange = _TDIconsData(0xE24B, 'currency_exchange'); + static const cursor_filled = _TDIconsData(0xE24C, 'cursor_filled'); + static const cursor = _TDIconsData(0xE24D, 'cursor'); + static const curtain_filled = _TDIconsData(0xE24E, 'curtain_filled'); + static const curtain = _TDIconsData(0xE24F, 'curtain'); + static const curve = _TDIconsData(0xE250, 'curve'); + static const cut_1 = _TDIconsData(0xE251, 'cut_1'); + static const cut = _TDIconsData(0xE252, 'cut'); + static const dam_1_filled = _TDIconsData(0xE253, 'dam_1_filled'); + static const dam_1 = _TDIconsData(0xE254, 'dam_1'); + static const dam_2_filled = _TDIconsData(0xE255, 'dam_2_filled'); + static const dam_2 = _TDIconsData(0xE256, 'dam_2'); + static const dam_3_filled = _TDIconsData(0xE257, 'dam_3_filled'); + static const dam_3 = _TDIconsData(0xE258, 'dam_3'); + static const dam_4_filled = _TDIconsData(0xE259, 'dam_4_filled'); + static const dam_4 = _TDIconsData(0xE25A, 'dam_4'); + static const dam_5_filled = _TDIconsData(0xE25B, 'dam_5_filled'); + static const dam_5 = _TDIconsData(0xE25C, 'dam_5'); + static const dam_6_filled = _TDIconsData(0xE25D, 'dam_6_filled'); + static const dam_6 = _TDIconsData(0xE25E, 'dam_6'); + static const dam_7_filled = _TDIconsData(0xE25F, 'dam_7_filled'); + static const dam_7 = _TDIconsData(0xE260, 'dam_7'); + static const dam_filled = _TDIconsData(0xE261, 'dam_filled'); + static const dam = _TDIconsData(0xE262, 'dam'); + static const dart_board_filled = _TDIconsData(0xE263, 'dart_board_filled'); + static const dart_board = _TDIconsData(0xE264, 'dart_board'); + static const dashboard_1_filled = _TDIconsData(0xE265, 'dashboard_1_filled'); + static const dashboard_1 = _TDIconsData(0xE266, 'dashboard_1'); + static const dashboard_filled = _TDIconsData(0xE267, 'dashboard_filled'); + static const dashboard = _TDIconsData(0xE268, 'dashboard'); + static const data_base_filled = _TDIconsData(0xE269, 'data_base_filled'); + static const data_base = _TDIconsData(0xE26A, 'data_base'); + static const data_checked_filled = _TDIconsData(0xE26B, 'data_checked_filled'); + static const data_checked = _TDIconsData(0xE26C, 'data_checked'); + static const data_display = _TDIconsData(0xE26D, 'data_display'); + static const data_error_filled = _TDIconsData(0xE26E, 'data_error_filled'); + static const data_error = _TDIconsData(0xE26F, 'data_error'); + static const data_filled = _TDIconsData(0xE270, 'data_filled'); + static const data_search_filled = _TDIconsData(0xE271, 'data_search_filled'); + static const data_search = _TDIconsData(0xE272, 'data_search'); + static const data = _TDIconsData(0xE273, 'data'); + static const delete_1_filled = _TDIconsData(0xE274, 'delete_1_filled'); + static const delete_1 = _TDIconsData(0xE275, 'delete_1'); + static const delete_filled = _TDIconsData(0xE276, 'delete_filled'); + static const delete_time_filled = _TDIconsData(0xE277, 'delete_time_filled'); + static const delete_time = _TDIconsData(0xE278, 'delete_time'); + static const delete = _TDIconsData(0xE279, 'delete'); + static const delta_filled = _TDIconsData(0xE27A, 'delta_filled'); + static const delta = _TDIconsData(0xE27B, 'delta'); + static const depressed_filled = _TDIconsData(0xE27C, 'depressed_filled'); + static const depressed = _TDIconsData(0xE27D, 'depressed'); + static const desktop_1_filled = _TDIconsData(0xE27E, 'desktop_1_filled'); + static const desktop_1 = _TDIconsData(0xE27F, 'desktop_1'); + static const desktop_filled = _TDIconsData(0xE280, 'desktop_filled'); + static const desktop = _TDIconsData(0xE281, 'desktop'); + static const despise_filled = _TDIconsData(0xE282, 'despise_filled'); + static const despise = _TDIconsData(0xE283, 'despise'); + static const device_filled = _TDIconsData(0xE284, 'device_filled'); + static const device = _TDIconsData(0xE285, 'device'); + static const discount_filled = _TDIconsData(0xE286, 'discount_filled'); + static const discount = _TDIconsData(0xE287, 'discount'); + static const dissatisfaction_filled = _TDIconsData(0xE288, 'dissatisfaction_filled'); + static const dissatisfaction = _TDIconsData(0xE289, 'dissatisfaction'); + static const divide = _TDIconsData(0xE28A, 'divide'); + static const dividers_1 = _TDIconsData(0xE28B, 'dividers_1'); + static const dividers = _TDIconsData(0xE28C, 'dividers'); + static const doge_filled = _TDIconsData(0xE28D, 'doge_filled'); + static const doge = _TDIconsData(0xE28E, 'doge'); + static const double_storey_filled = _TDIconsData(0xE28F, 'double_storey_filled'); + static const double_storey = _TDIconsData(0xE290, 'double_storey'); + static const download_1 = _TDIconsData(0xE291, 'download_1'); + static const download_2_filled = _TDIconsData(0xE292, 'download_2_filled'); + static const download_2 = _TDIconsData(0xE293, 'download_2'); + static const download = _TDIconsData(0xE294, 'download'); + static const downscale = _TDIconsData(0xE295, 'downscale'); + static const drag_drop = _TDIconsData(0xE296, 'drag_drop'); + static const drag_move = _TDIconsData(0xE297, 'drag_move'); + static const drink_filled = _TDIconsData(0xE298, 'drink_filled'); + static const drink = _TDIconsData(0xE299, 'drink'); + static const drumstick_filled = _TDIconsData(0xE29A, 'drumstick_filled'); + static const drumstick = _TDIconsData(0xE29B, 'drumstick'); + static const dv_filled = _TDIconsData(0xE29C, 'dv_filled'); + static const dv = _TDIconsData(0xE29D, 'dv'); + static const dvd_filled = _TDIconsData(0xE29E, 'dvd_filled'); + static const dvd = _TDIconsData(0xE29F, 'dvd'); + static const earphone_filled = _TDIconsData(0xE2A0, 'earphone_filled'); + static const earphone = _TDIconsData(0xE2A1, 'earphone'); + static const earth_filled = _TDIconsData(0xE2A2, 'earth_filled'); + static const earth = _TDIconsData(0xE2A3, 'earth'); + static const edit_1_filled = _TDIconsData(0xE2A4, 'edit_1_filled'); + static const edit_1 = _TDIconsData(0xE2A5, 'edit_1'); + static const edit_2_filled = _TDIconsData(0xE2A6, 'edit_2_filled'); + static const edit_2 = _TDIconsData(0xE2A7, 'edit_2'); + static const edit_filled = _TDIconsData(0xE2A8, 'edit_filled'); + static const edit_off_filled = _TDIconsData(0xE2A9, 'edit_off_filled'); + static const edit_off = _TDIconsData(0xE2AA, 'edit_off'); + static const edit = _TDIconsData(0xE2AB, 'edit'); + static const education_filled = _TDIconsData(0xE2AC, 'education_filled'); + static const education = _TDIconsData(0xE2AD, 'education'); + static const eggplant_filled = _TDIconsData(0xE2AE, 'eggplant_filled'); + static const eggplant = _TDIconsData(0xE2AF, 'eggplant'); + static const ellipsis = _TDIconsData(0xE2B0, 'ellipsis'); + static const emo_emotional_filled = _TDIconsData(0xE2B1, 'emo_emotional_filled'); + static const emo_emotional = _TDIconsData(0xE2B2, 'emo_emotional'); + static const enter = _TDIconsData(0xE2B3, 'enter'); + static const equal = _TDIconsData(0xE2B4, 'equal'); + static const error_circle_filled = _TDIconsData(0xE2B5, 'error_circle_filled'); + static const error_circle = _TDIconsData(0xE2B6, 'error_circle'); + static const error_triangle_filled = _TDIconsData(0xE2B7, 'error_triangle_filled'); + static const error_triangle = _TDIconsData(0xE2B8, 'error_triangle'); + static const error = _TDIconsData(0xE2B9, 'error'); + static const excited_1_filled = _TDIconsData(0xE2BA, 'excited_1_filled'); + static const excited_1 = _TDIconsData(0xE2BB, 'excited_1'); + static const excited_filled = _TDIconsData(0xE2BC, 'excited_filled'); + static const excited = _TDIconsData(0xE2BD, 'excited'); + static const expand_down_filled = _TDIconsData(0xE2BE, 'expand_down_filled'); + static const expand_down = _TDIconsData(0xE2BF, 'expand_down'); + static const expand_horizontal = _TDIconsData(0xE2C0, 'expand_horizontal'); + static const expand_up_filled = _TDIconsData(0xE2C1, 'expand_up_filled'); + static const expand_up = _TDIconsData(0xE2C2, 'expand_up'); + static const expand_vertical = _TDIconsData(0xE2C3, 'expand_vertical'); + static const explore_filled = _TDIconsData(0xE2C4, 'explore_filled'); + static const explore_off_filled = _TDIconsData(0xE2C5, 'explore_off_filled'); + static const explore_off = _TDIconsData(0xE2C6, 'explore_off'); + static const explore = _TDIconsData(0xE2C7, 'explore'); + static const exposure_filled = _TDIconsData(0xE2C8, 'exposure_filled'); + static const exposure = _TDIconsData(0xE2C9, 'exposure'); + static const extension_filled = _TDIconsData(0xE2CA, 'extension_filled'); + static const extension_off_filled = _TDIconsData(0xE2CB, 'extension_off_filled'); + static const extension_off = _TDIconsData(0xE2CC, 'extension_off'); + static const extension = _TDIconsData(0xE2CD, 'extension'); + static const face_retouching_filled = _TDIconsData(0xE2CE, 'face_retouching_filled'); + static const face_retouching = _TDIconsData(0xE2CF, 'face_retouching'); + static const fact_check_filled = _TDIconsData(0xE2D0, 'fact_check_filled'); + static const fact_check = _TDIconsData(0xE2D1, 'fact_check'); + static const fahrenheit_scale = _TDIconsData(0xE2D2, 'fahrenheit_scale'); + static const feel_at_ease_filled = _TDIconsData(0xE2D3, 'feel_at_ease_filled'); + static const feel_at_ease = _TDIconsData(0xE2D4, 'feel_at_ease'); + static const ferocious_filled = _TDIconsData(0xE2D5, 'ferocious_filled'); + static const ferocious = _TDIconsData(0xE2D6, 'ferocious'); + static const ferris_wheel_filled = _TDIconsData(0xE2D7, 'ferris_wheel_filled'); + static const ferris_wheel = _TDIconsData(0xE2D8, 'ferris_wheel'); + static const file_1_filled = _TDIconsData(0xE2D9, 'file_1_filled'); + static const file_1 = _TDIconsData(0xE2DA, 'file_1'); + static const file_add_1_filled = _TDIconsData(0xE2DB, 'file_add_1_filled'); + static const file_add_1 = _TDIconsData(0xE2DC, 'file_add_1'); + static const file_add_filled = _TDIconsData(0xE2DD, 'file_add_filled'); + static const file_add = _TDIconsData(0xE2DE, 'file_add'); + static const file_attachment_filled = _TDIconsData(0xE2DF, 'file_attachment_filled'); + static const file_attachment = _TDIconsData(0xE2E0, 'file_attachment'); + static const file_blocked_filled = _TDIconsData(0xE2E1, 'file_blocked_filled'); + static const file_blocked = _TDIconsData(0xE2E2, 'file_blocked'); + static const file_code_1_filled = _TDIconsData(0xE2E3, 'file_code_1_filled'); + static const file_code_1 = _TDIconsData(0xE2E4, 'file_code_1'); + static const file_code_filled = _TDIconsData(0xE2E5, 'file_code_filled'); + static const file_code = _TDIconsData(0xE2E6, 'file_code'); + static const file_copy_filled = _TDIconsData(0xE2E7, 'file_copy_filled'); + static const file_copy = _TDIconsData(0xE2E8, 'file_copy'); + static const file_download_filled = _TDIconsData(0xE2E9, 'file_download_filled'); + static const file_download = _TDIconsData(0xE2EA, 'file_download'); + static const file_excel_filled = _TDIconsData(0xE2EB, 'file_excel_filled'); + static const file_excel = _TDIconsData(0xE2EC, 'file_excel'); + static const file_export_filled = _TDIconsData(0xE2ED, 'file_export_filled'); + static const file_export = _TDIconsData(0xE2EE, 'file_export'); + static const file_filled = _TDIconsData(0xE2EF, 'file_filled'); + static const file_icon_filled = _TDIconsData(0xE2F0, 'file_icon_filled'); + static const file_icon = _TDIconsData(0xE2F1, 'file_icon'); + static const file_image_filled = _TDIconsData(0xE2F2, 'file_image_filled'); + static const file_image = _TDIconsData(0xE2F3, 'file_image'); + static const file_import_filled = _TDIconsData(0xE2F4, 'file_import_filled'); + static const file_import = _TDIconsData(0xE2F5, 'file_import'); + static const file_locked_filled = _TDIconsData(0xE2F6, 'file_locked_filled'); + static const file_locked = _TDIconsData(0xE2F7, 'file_locked'); + static const file_minus_filled = _TDIconsData(0xE2F8, 'file_minus_filled'); + static const file_minus = _TDIconsData(0xE2F9, 'file_minus'); + static const file_music_filled = _TDIconsData(0xE2FA, 'file_music_filled'); + static const file_music = _TDIconsData(0xE2FB, 'file_music'); + static const file_onenote_filled = _TDIconsData(0xE2FC, 'file_onenote_filled'); + static const file_onenote = _TDIconsData(0xE2FD, 'file_onenote'); + static const file_outlook_filled = _TDIconsData(0xE2FE, 'file_outlook_filled'); + static const file_outlook = _TDIconsData(0xE2FF, 'file_outlook'); + static const file_paste_filled = _TDIconsData(0xE300, 'file_paste_filled'); + static const file_paste = _TDIconsData(0xE301, 'file_paste'); + static const file_pdf_filled = _TDIconsData(0xE302, 'file_pdf_filled'); + static const file_pdf = _TDIconsData(0xE303, 'file_pdf'); + static const file_powerpoint_filled = _TDIconsData(0xE304, 'file_powerpoint_filled'); + static const file_powerpoint = _TDIconsData(0xE305, 'file_powerpoint'); + static const file_restore_filled = _TDIconsData(0xE306, 'file_restore_filled'); + static const file_restore = _TDIconsData(0xE307, 'file_restore'); + static const file_safety_filled = _TDIconsData(0xE308, 'file_safety_filled'); + static const file_safety = _TDIconsData(0xE309, 'file_safety'); + static const file_search_filled = _TDIconsData(0xE30A, 'file_search_filled'); + static const file_search = _TDIconsData(0xE30B, 'file_search'); + static const file_setting_filled = _TDIconsData(0xE30C, 'file_setting_filled'); + static const file_setting = _TDIconsData(0xE30D, 'file_setting'); + static const file_teams_filled = _TDIconsData(0xE30E, 'file_teams_filled'); + static const file_teams = _TDIconsData(0xE30F, 'file_teams'); + static const file_transmit_double_filled = _TDIconsData(0xE310, 'file_transmit_double_filled'); + static const file_transmit_double = _TDIconsData(0xE311, 'file_transmit_double'); + static const file_transmit_filled = _TDIconsData(0xE312, 'file_transmit_filled'); + static const file_transmit = _TDIconsData(0xE313, 'file_transmit'); + static const file_unknown_filled = _TDIconsData(0xE314, 'file_unknown_filled'); + static const file_unknown = _TDIconsData(0xE315, 'file_unknown'); + static const file_unlocked_filled = _TDIconsData(0xE316, 'file_unlocked_filled'); + static const file_unlocked = _TDIconsData(0xE317, 'file_unlocked'); + static const file_word_filled = _TDIconsData(0xE318, 'file_word_filled'); + static const file_word = _TDIconsData(0xE319, 'file_word'); + static const file_zip_filled = _TDIconsData(0xE31A, 'file_zip_filled'); + static const file_zip = _TDIconsData(0xE31B, 'file_zip'); + static const file = _TDIconsData(0xE31C, 'file'); + static const fill_color_1_filled = _TDIconsData(0xE31D, 'fill_color_1_filled'); + static const fill_color_1 = _TDIconsData(0xE31E, 'fill_color_1'); + static const fill_color_filled = _TDIconsData(0xE31F, 'fill_color_filled'); + static const fill_color = _TDIconsData(0xE320, 'fill_color'); + static const film_1_filled = _TDIconsData(0xE321, 'film_1_filled'); + static const film_1 = _TDIconsData(0xE322, 'film_1'); + static const film_filled = _TDIconsData(0xE323, 'film_filled'); + static const film = _TDIconsData(0xE324, 'film'); + static const filter_1_filled = _TDIconsData(0xE325, 'filter_1_filled'); + static const filter_1 = _TDIconsData(0xE326, 'filter_1'); + static const filter_2_filled = _TDIconsData(0xE327, 'filter_2_filled'); + static const filter_2 = _TDIconsData(0xE328, 'filter_2'); + static const filter_3_filled = _TDIconsData(0xE329, 'filter_3_filled'); + static const filter_3 = _TDIconsData(0xE32A, 'filter_3'); + static const filter_clear_filled = _TDIconsData(0xE32B, 'filter_clear_filled'); + static const filter_clear = _TDIconsData(0xE32C, 'filter_clear'); + static const filter_filled = _TDIconsData(0xE32D, 'filter_filled'); + static const filter_off_filled = _TDIconsData(0xE32E, 'filter_off_filled'); + static const filter_off = _TDIconsData(0xE32F, 'filter_off'); + static const filter_sort_filled = _TDIconsData(0xE330, 'filter_sort_filled'); + static const filter_sort = _TDIconsData(0xE331, 'filter_sort'); + static const filter = _TDIconsData(0xE332, 'filter'); + static const fingerprint_1 = _TDIconsData(0xE333, 'fingerprint_1'); + static const fingerprint_2 = _TDIconsData(0xE334, 'fingerprint_2'); + static const fingerprint_3 = _TDIconsData(0xE335, 'fingerprint_3'); + static const fingerprint = _TDIconsData(0xE336, 'fingerprint'); + static const fish_filled = _TDIconsData(0xE337, 'fish_filled'); + static const fish = _TDIconsData(0xE338, 'fish'); + static const flag_1_filled = _TDIconsData(0xE339, 'flag_1_filled'); + static const flag_1 = _TDIconsData(0xE33A, 'flag_1'); + static const flag_2_filled = _TDIconsData(0xE33B, 'flag_2_filled'); + static const flag_2 = _TDIconsData(0xE33C, 'flag_2'); + static const flag_3_filled = _TDIconsData(0xE33D, 'flag_3_filled'); + static const flag_3 = _TDIconsData(0xE33E, 'flag_3'); + static const flag_4_filled = _TDIconsData(0xE33F, 'flag_4_filled'); + static const flag_4 = _TDIconsData(0xE340, 'flag_4'); + static const flag_filled = _TDIconsData(0xE341, 'flag_filled'); + static const flag = _TDIconsData(0xE342, 'flag'); + static const flashlight_filled = _TDIconsData(0xE343, 'flashlight_filled'); + static const flashlight = _TDIconsData(0xE344, 'flashlight'); + static const flight_landing_filled = _TDIconsData(0xE345, 'flight_landing_filled'); + static const flight_landing = _TDIconsData(0xE346, 'flight_landing'); + static const flight_takeoff_filled = _TDIconsData(0xE347, 'flight_takeoff_filled'); + static const flight_takeoff = _TDIconsData(0xE348, 'flight_takeoff'); + static const flip_smiling_face_filled = _TDIconsData(0xE349, 'flip_smiling_face_filled'); + static const flip_smiling_face = _TDIconsData(0xE34A, 'flip_smiling_face'); + static const flip_to_back_filled = _TDIconsData(0xE34B, 'flip_to_back_filled'); + static const flip_to_back = _TDIconsData(0xE34C, 'flip_to_back'); + static const flip_to_front_filled = _TDIconsData(0xE34D, 'flip_to_front_filled'); + static const flip_to_front = _TDIconsData(0xE34E, 'flip_to_front'); + static const focus_filled = _TDIconsData(0xE34F, 'focus_filled'); + static const focus = _TDIconsData(0xE350, 'focus'); + static const fog_filled = _TDIconsData(0xE351, 'fog_filled'); + static const fog_night_filled = _TDIconsData(0xE352, 'fog_night_filled'); + static const fog_night = _TDIconsData(0xE353, 'fog_night'); + static const fog_sunny_filled = _TDIconsData(0xE354, 'fog_sunny_filled'); + static const fog_sunny = _TDIconsData(0xE355, 'fog_sunny'); + static const fog = _TDIconsData(0xE356, 'fog'); + static const folder_1_filled = _TDIconsData(0xE357, 'folder_1_filled'); + static const folder_1 = _TDIconsData(0xE358, 'folder_1'); + static const folder_add_1_filled = _TDIconsData(0xE359, 'folder_add_1_filled'); + static const folder_add_1 = _TDIconsData(0xE35A, 'folder_add_1'); + static const folder_add_filled = _TDIconsData(0xE35B, 'folder_add_filled'); + static const folder_add = _TDIconsData(0xE35C, 'folder_add'); + static const folder_blocked_filled = _TDIconsData(0xE35D, 'folder_blocked_filled'); + static const folder_blocked = _TDIconsData(0xE35E, 'folder_blocked'); + static const folder_details_filled = _TDIconsData(0xE35F, 'folder_details_filled'); + static const folder_details = _TDIconsData(0xE360, 'folder_details'); + static const folder_export_filled = _TDIconsData(0xE361, 'folder_export_filled'); + static const folder_export = _TDIconsData(0xE362, 'folder_export'); + static const folder_filled = _TDIconsData(0xE363, 'folder_filled'); + static const folder_import_filled = _TDIconsData(0xE364, 'folder_import_filled'); + static const folder_import = _TDIconsData(0xE365, 'folder_import'); + static const folder_locked_filled = _TDIconsData(0xE366, 'folder_locked_filled'); + static const folder_locked = _TDIconsData(0xE367, 'folder_locked'); + static const folder_minus_filled = _TDIconsData(0xE368, 'folder_minus_filled'); + static const folder_minus = _TDIconsData(0xE369, 'folder_minus'); + static const folder_move_filled = _TDIconsData(0xE36A, 'folder_move_filled'); + static const folder_move = _TDIconsData(0xE36B, 'folder_move'); + static const folder_off_filled = _TDIconsData(0xE36C, 'folder_off_filled'); + static const folder_off = _TDIconsData(0xE36D, 'folder_off'); + static const folder_open_1_filled = _TDIconsData(0xE36E, 'folder_open_1_filled'); + static const folder_open_1 = _TDIconsData(0xE36F, 'folder_open_1'); + static const folder_open_filled = _TDIconsData(0xE370, 'folder_open_filled'); + static const folder_open = _TDIconsData(0xE371, 'folder_open'); + static const folder_search_filled = _TDIconsData(0xE372, 'folder_search_filled'); + static const folder_search = _TDIconsData(0xE373, 'folder_search'); + static const folder_setting_filled = _TDIconsData(0xE374, 'folder_setting_filled'); + static const folder_setting = _TDIconsData(0xE375, 'folder_setting'); + static const folder_shared_filled = _TDIconsData(0xE376, 'folder_shared_filled'); + static const folder_shared = _TDIconsData(0xE377, 'folder_shared'); + static const folder_unlocked_filled = _TDIconsData(0xE378, 'folder_unlocked_filled'); + static const folder_unlocked = _TDIconsData(0xE379, 'folder_unlocked'); + static const folder_zip_filled = _TDIconsData(0xE37A, 'folder_zip_filled'); + static const folder_zip = _TDIconsData(0xE37B, 'folder_zip'); + static const folder = _TDIconsData(0xE37C, 'folder'); + static const forest_filled = _TDIconsData(0xE37D, 'forest_filled'); + static const forest = _TDIconsData(0xE37E, 'forest'); + static const fork_filled = _TDIconsData(0xE37F, 'fork_filled'); + static const fork = _TDIconsData(0xE380, 'fork'); + static const form_filled = _TDIconsData(0xE381, 'form_filled'); + static const form = _TDIconsData(0xE382, 'form'); + static const format_horizontal_align_bottom = _TDIconsData(0xE383, 'format_horizontal_align_bottom'); + static const format_horizontal_align_center = _TDIconsData(0xE384, 'format_horizontal_align_center'); + static const format_horizontal_align_top = _TDIconsData(0xE385, 'format_horizontal_align_top'); + static const format_vertical_align_center = _TDIconsData(0xE386, 'format_vertical_align_center'); + static const format_vertical_align_left = _TDIconsData(0xE387, 'format_vertical_align_left'); + static const format_vertical_align_right = _TDIconsData(0xE388, 'format_vertical_align_right'); + static const forward_filled = _TDIconsData(0xE389, 'forward_filled'); + static const forward = _TDIconsData(0xE38A, 'forward'); + static const frame_1_filled = _TDIconsData(0xE38B, 'frame_1_filled'); + static const frame_1 = _TDIconsData(0xE38C, 'frame_1'); + static const frame_filled = _TDIconsData(0xE38D, 'frame_filled'); + static const frame = _TDIconsData(0xE38E, 'frame'); + static const fries_filled = _TDIconsData(0xE38F, 'fries_filled'); + static const fries = _TDIconsData(0xE390, 'fries'); + static const fullscreen_1 = _TDIconsData(0xE391, 'fullscreen_1'); + static const fullscreen_2 = _TDIconsData(0xE392, 'fullscreen_2'); + static const fullscreen_exit_1 = _TDIconsData(0xE393, 'fullscreen_exit_1'); + static const fullscreen_exit = _TDIconsData(0xE394, 'fullscreen_exit'); + static const fullscreen = _TDIconsData(0xE395, 'fullscreen'); + static const function_curve = _TDIconsData(0xE396, 'function_curve'); + static const functions_1 = _TDIconsData(0xE397, 'functions_1'); + static const functions = _TDIconsData(0xE398, 'functions'); + static const gamepad_1_filled = _TDIconsData(0xE399, 'gamepad_1_filled'); + static const gamepad_1 = _TDIconsData(0xE39A, 'gamepad_1'); + static const gamepad_filled = _TDIconsData(0xE39B, 'gamepad_filled'); + static const gamepad = _TDIconsData(0xE39C, 'gamepad'); + static const gamma = _TDIconsData(0xE39D, 'gamma'); + static const garlic_filled = _TDIconsData(0xE39E, 'garlic_filled'); + static const garlic = _TDIconsData(0xE39F, 'garlic'); + static const gender_female = _TDIconsData(0xE3A0, 'gender_female'); + static const gender_male = _TDIconsData(0xE3A1, 'gender_male'); + static const gesture_applause_filled = _TDIconsData(0xE3A2, 'gesture_applause_filled'); + static const gesture_applause = _TDIconsData(0xE3A3, 'gesture_applause'); + static const gesture_click_filled = _TDIconsData(0xE3A4, 'gesture_click_filled'); + static const gesture_click = _TDIconsData(0xE3A5, 'gesture_click'); + static const gesture_down_filled = _TDIconsData(0xE3A6, 'gesture_down_filled'); + static const gesture_down = _TDIconsData(0xE3A7, 'gesture_down'); + static const gesture_expansion_filled = _TDIconsData(0xE3A8, 'gesture_expansion_filled'); + static const gesture_expansion = _TDIconsData(0xE3A9, 'gesture_expansion'); + static const gesture_left_filled = _TDIconsData(0xE3AA, 'gesture_left_filled'); + static const gesture_left_slip_filled = _TDIconsData(0xE3AB, 'gesture_left_slip_filled'); + static const gesture_left_slip = _TDIconsData(0xE3AC, 'gesture_left_slip'); + static const gesture_left = _TDIconsData(0xE3AD, 'gesture_left'); + static const gesture_open_filled = _TDIconsData(0xE3AE, 'gesture_open_filled'); + static const gesture_open = _TDIconsData(0xE3AF, 'gesture_open'); + static const gesture_pray_filled = _TDIconsData(0xE3B0, 'gesture_pray_filled'); + static const gesture_pray = _TDIconsData(0xE3B1, 'gesture_pray'); + static const gesture_press_filled = _TDIconsData(0xE3B2, 'gesture_press_filled'); + static const gesture_press = _TDIconsData(0xE3B3, 'gesture_press'); + static const gesture_ranslation_filled = _TDIconsData(0xE3B4, 'gesture_ranslation_filled'); + static const gesture_ranslation = _TDIconsData(0xE3B5, 'gesture_ranslation'); + static const gesture_right_filled = _TDIconsData(0xE3B6, 'gesture_right_filled'); + static const gesture_right_slip_filled = _TDIconsData(0xE3B7, 'gesture_right_slip_filled'); + static const gesture_right_slip = _TDIconsData(0xE3B8, 'gesture_right_slip'); + static const gesture_right = _TDIconsData(0xE3B9, 'gesture_right'); + static const gesture_slide_left_and_right_filled = _TDIconsData(0xE3BA, 'gesture_slide_left_and_right_filled'); + static const gesture_slide_left_and_right = _TDIconsData(0xE3BB, 'gesture_slide_left_and_right'); + static const gesture_slide_up_filled = _TDIconsData(0xE3BC, 'gesture_slide_up_filled'); + static const gesture_slide_up = _TDIconsData(0xE3BD, 'gesture_slide_up'); + static const gesture_typing_filled = _TDIconsData(0xE3BE, 'gesture_typing_filled'); + static const gesture_typing = _TDIconsData(0xE3BF, 'gesture_typing'); + static const gesture_up_and_down_filled = _TDIconsData(0xE3C0, 'gesture_up_and_down_filled'); + static const gesture_up_and_down = _TDIconsData(0xE3C1, 'gesture_up_and_down'); + static const gesture_up_filled = _TDIconsData(0xE3C2, 'gesture_up_filled'); + static const gesture_up = _TDIconsData(0xE3C3, 'gesture_up'); + static const gesture_wipe_down_filled = _TDIconsData(0xE3C4, 'gesture_wipe_down_filled'); + static const gesture_wipe_down = _TDIconsData(0xE3C5, 'gesture_wipe_down'); + static const gift_filled = _TDIconsData(0xE3C6, 'gift_filled'); + static const gift = _TDIconsData(0xE3C7, 'gift'); + static const giggle_filled = _TDIconsData(0xE3C8, 'giggle_filled'); + static const giggle = _TDIconsData(0xE3C9, 'giggle'); + static const git_branch_filled = _TDIconsData(0xE3CA, 'git_branch_filled'); + static const git_branch = _TDIconsData(0xE3CB, 'git_branch'); + static const git_commit_filled = _TDIconsData(0xE3CC, 'git_commit_filled'); + static const git_commit = _TDIconsData(0xE3CD, 'git_commit'); + static const git_merge_filled = _TDIconsData(0xE3CE, 'git_merge_filled'); + static const git_merge = _TDIconsData(0xE3CF, 'git_merge'); + static const git_pull_request_filled = _TDIconsData(0xE3D0, 'git_pull_request_filled'); + static const git_pull_request = _TDIconsData(0xE3D1, 'git_pull_request'); + static const git_repository_commits_filled = _TDIconsData(0xE3D2, 'git_repository_commits_filled'); + static const git_repository_commits = _TDIconsData(0xE3D3, 'git_repository_commits'); + static const git_repository_filled = _TDIconsData(0xE3D4, 'git_repository_filled'); + static const git_repository_private_filled = _TDIconsData(0xE3D5, 'git_repository_private_filled'); + static const git_repository_private = _TDIconsData(0xE3D6, 'git_repository_private'); + static const git_repository = _TDIconsData(0xE3D7, 'git_repository'); + static const gps_filled = _TDIconsData(0xE3D8, 'gps_filled'); + static const gps = _TDIconsData(0xE3D9, 'gps'); + static const grape_filled = _TDIconsData(0xE3DA, 'grape_filled'); + static const grape = _TDIconsData(0xE3DB, 'grape'); + static const greater_than_or_equal = _TDIconsData(0xE3DC, 'greater_than_or_equal'); + static const greater_than = _TDIconsData(0xE3DD, 'greater_than'); + static const green_onion = _TDIconsData(0xE3DE, 'green_onion'); + static const grid_add_filled = _TDIconsData(0xE3DF, 'grid_add_filled'); + static const grid_add = _TDIconsData(0xE3E0, 'grid_add'); + static const grid_view_filled = _TDIconsData(0xE3E1, 'grid_view_filled'); + static const grid_view = _TDIconsData(0xE3E2, 'grid_view'); + static const guitar_filled = _TDIconsData(0xE3E3, 'guitar_filled'); + static const guitar = _TDIconsData(0xE3E4, 'guitar'); + static const hamburger_filled = _TDIconsData(0xE3E5, 'hamburger_filled'); + static const hamburger = _TDIconsData(0xE3E6, 'hamburger'); + static const happy_filled = _TDIconsData(0xE3E7, 'happy_filled'); + static const happy = _TDIconsData(0xE3E8, 'happy'); + static const hard_disk_storage_filled = _TDIconsData(0xE3E9, 'hard_disk_storage_filled'); + static const hard_disk_storage = _TDIconsData(0xE3EA, 'hard_disk_storage'); + static const hard_drive_filled = _TDIconsData(0xE3EB, 'hard_drive_filled'); + static const hard_drive = _TDIconsData(0xE3EC, 'hard_drive'); + static const hashtag = _TDIconsData(0xE3ED, 'hashtag'); + static const hd_filled = _TDIconsData(0xE3EE, 'hd_filled'); + static const hd = _TDIconsData(0xE3EF, 'hd'); + static const heart_filled = _TDIconsData(0xE3F0, 'heart_filled'); + static const heart = _TDIconsData(0xE3F1, 'heart'); + static const help_circle_filled = _TDIconsData(0xE3F2, 'help_circle_filled'); + static const help_circle = _TDIconsData(0xE3F3, 'help_circle'); + static const help_rectangle_filled = _TDIconsData(0xE3F4, 'help_rectangle_filled'); + static const help_rectangle = _TDIconsData(0xE3F5, 'help_rectangle'); + static const help = _TDIconsData(0xE3F6, 'help'); + static const highlight_1_filled = _TDIconsData(0xE3F7, 'highlight_1_filled'); + static const highlight_1 = _TDIconsData(0xE3F8, 'highlight_1'); + static const highlight = _TDIconsData(0xE3F9, 'highlight'); + static const history_setting = _TDIconsData(0xE3FA, 'history_setting'); + static const history = _TDIconsData(0xE3FB, 'history'); + static const home_filled = _TDIconsData(0xE3FC, 'home_filled'); + static const home = _TDIconsData(0xE3FD, 'home'); + static const horizontal_filled = _TDIconsData(0xE3FE, 'horizontal_filled'); + static const horizontal = _TDIconsData(0xE3FF, 'horizontal'); + static const hospital_1_filled = _TDIconsData(0xE400, 'hospital_1_filled'); + static const hospital_1 = _TDIconsData(0xE401, 'hospital_1'); + static const hospital_filled = _TDIconsData(0xE402, 'hospital_filled'); + static const hospital = _TDIconsData(0xE403, 'hospital'); + static const hotspot_wave_filled = _TDIconsData(0xE404, 'hotspot_wave_filled'); + static const hotspot_wave = _TDIconsData(0xE405, 'hotspot_wave'); + static const hourglass_filled = _TDIconsData(0xE406, 'hourglass_filled'); + static const hourglass = _TDIconsData(0xE407, 'hourglass'); + static const houses_1_filled = _TDIconsData(0xE408, 'houses_1_filled'); + static const houses_1 = _TDIconsData(0xE409, 'houses_1'); + static const houses_2_filled = _TDIconsData(0xE40A, 'houses_2_filled'); + static const houses_2 = _TDIconsData(0xE40B, 'houses_2'); + static const houses_filled = _TDIconsData(0xE40C, 'houses_filled'); + static const houses = _TDIconsData(0xE40D, 'houses'); + static const html5_filled = _TDIconsData(0xE40E, 'html5_filled'); + static const html5 = _TDIconsData(0xE40F, 'html5'); + static const https_filled = _TDIconsData(0xE410, 'https_filled'); + static const https = _TDIconsData(0xE411, 'https'); + static const ice_cream_filled = _TDIconsData(0xE412, 'ice_cream_filled'); + static const ice_cream = _TDIconsData(0xE413, 'ice_cream'); + static const icon_filled = _TDIconsData(0xE414, 'icon_filled'); + static const icon = _TDIconsData(0xE415, 'icon'); + static const image_1_filled = _TDIconsData(0xE416, 'image_1_filled'); + static const image_1 = _TDIconsData(0xE417, 'image_1'); + static const image_add_filled = _TDIconsData(0xE418, 'image_add_filled'); + static const image_add = _TDIconsData(0xE419, 'image_add'); + static const image_edit_filled = _TDIconsData(0xE41A, 'image_edit_filled'); + static const image_edit = _TDIconsData(0xE41B, 'image_edit'); + static const image_error_filled = _TDIconsData(0xE41C, 'image_error_filled'); + static const image_error = _TDIconsData(0xE41D, 'image_error'); + static const image_filled = _TDIconsData(0xE41E, 'image_filled'); + static const image_off_filled = _TDIconsData(0xE41F, 'image_off_filled'); + static const image_off = _TDIconsData(0xE420, 'image_off'); + static const image_search_filled = _TDIconsData(0xE421, 'image_search_filled'); + static const image_search = _TDIconsData(0xE422, 'image_search'); + static const image = _TDIconsData(0xE423, 'image'); + static const indent_left = _TDIconsData(0xE424, 'indent_left'); + static const indent_right = _TDIconsData(0xE425, 'indent_right'); + static const indicator_filled = _TDIconsData(0xE426, 'indicator_filled'); + static const indicator = _TDIconsData(0xE427, 'indicator'); + static const info_circle_filled = _TDIconsData(0xE428, 'info_circle_filled'); + static const info_circle = _TDIconsData(0xE429, 'info_circle'); + static const ink_filled = _TDIconsData(0xE42A, 'ink_filled'); + static const ink = _TDIconsData(0xE42B, 'ink'); + static const install_desktop_filled = _TDIconsData(0xE42C, 'install_desktop_filled'); + static const install_desktop = _TDIconsData(0xE42D, 'install_desktop'); + static const install_filled = _TDIconsData(0xE42E, 'install_filled'); + static const install_mobile_filled = _TDIconsData(0xE42F, 'install_mobile_filled'); + static const install_mobile = _TDIconsData(0xE430, 'install_mobile'); + static const install = _TDIconsData(0xE431, 'install'); + static const institution_checked_filled = _TDIconsData(0xE432, 'institution_checked_filled'); + static const institution_checked = _TDIconsData(0xE433, 'institution_checked'); + static const institution_filled = _TDIconsData(0xE434, 'institution_filled'); + static const institution = _TDIconsData(0xE435, 'institution'); + static const internet_filled = _TDIconsData(0xE436, 'internet_filled'); + static const internet = _TDIconsData(0xE437, 'internet'); + static const ipod_filled = _TDIconsData(0xE438, 'ipod_filled'); + static const ipod = _TDIconsData(0xE439, 'ipod'); + static const joyful_filled = _TDIconsData(0xE43A, 'joyful_filled'); + static const joyful = _TDIconsData(0xE43B, 'joyful'); + static const jump_double = _TDIconsData(0xE43C, 'jump_double'); + static const jump_off = _TDIconsData(0xE43D, 'jump_off'); + static const jump = _TDIconsData(0xE43E, 'jump'); + static const key_filled = _TDIconsData(0xE43F, 'key_filled'); + static const key = _TDIconsData(0xE440, 'key'); + static const keyboard_filled = _TDIconsData(0xE441, 'keyboard_filled'); + static const keyboard = _TDIconsData(0xE442, 'keyboard'); + static const laptop_filled = _TDIconsData(0xE443, 'laptop_filled'); + static const laptop = _TDIconsData(0xE444, 'laptop'); + static const layers_filled = _TDIconsData(0xE445, 'layers_filled'); + static const layers = _TDIconsData(0xE446, 'layers'); + static const layout_filled = _TDIconsData(0xE447, 'layout_filled'); + static const layout = _TDIconsData(0xE448, 'layout'); + static const leaderboard_filled = _TDIconsData(0xE449, 'leaderboard_filled'); + static const leaderboard = _TDIconsData(0xE44A, 'leaderboard'); + static const lemon_filled = _TDIconsData(0xE44B, 'lemon_filled'); + static const lemon_slice_filled = _TDIconsData(0xE44C, 'lemon_slice_filled'); + static const lemon_slice = _TDIconsData(0xE44D, 'lemon_slice'); + static const lemon = _TDIconsData(0xE44E, 'lemon'); + static const less_than_or_equal = _TDIconsData(0xE44F, 'less_than_or_equal'); + static const less_than = _TDIconsData(0xE450, 'less_than'); + static const letters_a = _TDIconsData(0xE451, 'letters_a'); + static const letters_b = _TDIconsData(0xE452, 'letters_b'); + static const letters_c = _TDIconsData(0xE453, 'letters_c'); + static const letters_d = _TDIconsData(0xE454, 'letters_d'); + static const letters_e = _TDIconsData(0xE455, 'letters_e'); + static const letters_f = _TDIconsData(0xE456, 'letters_f'); + static const letters_g = _TDIconsData(0xE457, 'letters_g'); + static const letters_h = _TDIconsData(0xE458, 'letters_h'); + static const letters_i = _TDIconsData(0xE459, 'letters_i'); + static const letters_j = _TDIconsData(0xE45A, 'letters_j'); + static const letters_k = _TDIconsData(0xE45B, 'letters_k'); + static const letters_l = _TDIconsData(0xE45C, 'letters_l'); + static const letters_m = _TDIconsData(0xE45D, 'letters_m'); + static const letters_n = _TDIconsData(0xE45E, 'letters_n'); + static const letters_o = _TDIconsData(0xE45F, 'letters_o'); + static const letters_p = _TDIconsData(0xE460, 'letters_p'); + static const letters_q = _TDIconsData(0xE461, 'letters_q'); + static const letters_r = _TDIconsData(0xE462, 'letters_r'); + static const letters_s = _TDIconsData(0xE463, 'letters_s'); + static const letters_t = _TDIconsData(0xE464, 'letters_t'); + static const letters_u = _TDIconsData(0xE465, 'letters_u'); + static const letters_v = _TDIconsData(0xE466, 'letters_v'); + static const letters_w = _TDIconsData(0xE467, 'letters_w'); + static const letters_x = _TDIconsData(0xE468, 'letters_x'); + static const letters_y = _TDIconsData(0xE469, 'letters_y'); + static const letters_z = _TDIconsData(0xE46A, 'letters_z'); + static const lightbulb_circle_filled = _TDIconsData(0xE46B, 'lightbulb_circle_filled'); + static const lightbulb_circle = _TDIconsData(0xE46C, 'lightbulb_circle'); + static const lightbulb_filled = _TDIconsData(0xE46D, 'lightbulb_filled'); + static const lightbulb = _TDIconsData(0xE46E, 'lightbulb'); + static const lighthouse_1_filled = _TDIconsData(0xE46F, 'lighthouse_1_filled'); + static const lighthouse_1 = _TDIconsData(0xE470, 'lighthouse_1'); + static const lighthouse_2_filled = _TDIconsData(0xE471, 'lighthouse_2_filled'); + static const lighthouse_2 = _TDIconsData(0xE472, 'lighthouse_2'); + static const lighthouse_filled = _TDIconsData(0xE473, 'lighthouse_filled'); + static const lighthouse = _TDIconsData(0xE474, 'lighthouse'); + static const lighting_circle_filled = _TDIconsData(0xE475, 'lighting_circle_filled'); + static const lighting_circle = _TDIconsData(0xE476, 'lighting_circle'); + static const line_height = _TDIconsData(0xE477, 'line_height'); + static const link_1 = _TDIconsData(0xE478, 'link_1'); + static const link_unlink = _TDIconsData(0xE479, 'link_unlink'); + static const link = _TDIconsData(0xE47A, 'link'); + static const liquor_filled = _TDIconsData(0xE47B, 'liquor_filled'); + static const liquor = _TDIconsData(0xE47C, 'liquor'); + static const list_numbered = _TDIconsData(0xE47D, 'list_numbered'); + static const list = _TDIconsData(0xE47E, 'list'); + static const load = _TDIconsData(0xE47F, 'load'); + static const loading = _TDIconsData(0xE480, 'loading'); + static const location_1_filled = _TDIconsData(0xE481, 'location_1_filled'); + static const location_1 = _TDIconsData(0xE482, 'location_1'); + static const location_enlargement_filled = _TDIconsData(0xE483, 'location_enlargement_filled'); + static const location_enlargement = _TDIconsData(0xE484, 'location_enlargement'); + static const location_error_filled = _TDIconsData(0xE485, 'location_error_filled'); + static const location_error = _TDIconsData(0xE486, 'location_error'); + static const location_filled = _TDIconsData(0xE487, 'location_filled'); + static const location_parking_place_filled = _TDIconsData(0xE488, 'location_parking_place_filled'); + static const location_parking_place = _TDIconsData(0xE489, 'location_parking_place'); + static const location_reduction_filled = _TDIconsData(0xE48A, 'location_reduction_filled'); + static const location_reduction = _TDIconsData(0xE48B, 'location_reduction'); + static const location_setting_filled = _TDIconsData(0xE48C, 'location_setting_filled'); + static const location_setting = _TDIconsData(0xE48D, 'location_setting'); + static const location = _TDIconsData(0xE48E, 'location'); + static const lock_off_filled = _TDIconsData(0xE48F, 'lock_off_filled'); + static const lock_off = _TDIconsData(0xE490, 'lock_off'); + static const lock_on_filled = _TDIconsData(0xE491, 'lock_on_filled'); + static const lock_on = _TDIconsData(0xE492, 'lock_on'); + static const lock_time_filled = _TDIconsData(0xE493, 'lock_time_filled'); + static const lock_time = _TDIconsData(0xE494, 'lock_time'); + static const login = _TDIconsData(0xE495, 'login'); + static const logo_adobe_illustrate_filled = _TDIconsData(0xE496, 'logo_adobe_illustrate_filled'); + static const logo_adobe_illustrate = _TDIconsData(0xE497, 'logo_adobe_illustrate'); + static const logo_adobe_lightroom_filled = _TDIconsData(0xE498, 'logo_adobe_lightroom_filled'); + static const logo_adobe_lightroom = _TDIconsData(0xE499, 'logo_adobe_lightroom'); + static const logo_adobe_photoshop_filled = _TDIconsData(0xE49A, 'logo_adobe_photoshop_filled'); + static const logo_adobe_photoshop = _TDIconsData(0xE49B, 'logo_adobe_photoshop'); + static const logo_android_filled = _TDIconsData(0xE49C, 'logo_android_filled'); + static const logo_android = _TDIconsData(0xE49D, 'logo_android'); + static const logo_apple_filled = _TDIconsData(0xE49E, 'logo_apple_filled'); + static const logo_apple = _TDIconsData(0xE49F, 'logo_apple'); + static const logo_behance = _TDIconsData(0xE4A0, 'logo_behance'); + static const logo_chrome_filled = _TDIconsData(0xE4A1, 'logo_chrome_filled'); + static const logo_chrome = _TDIconsData(0xE4A2, 'logo_chrome'); + static const logo_cinema4d_filled = _TDIconsData(0xE4A3, 'logo_cinema4d_filled'); + static const logo_cinema4d = _TDIconsData(0xE4A4, 'logo_cinema4d'); + static const logo_codepen = _TDIconsData(0xE4A5, 'logo_codepen'); + static const logo_codesandbox = _TDIconsData(0xE4A6, 'logo_codesandbox'); + static const logo_dribbble_filled = _TDIconsData(0xE4A7, 'logo_dribbble_filled'); + static const logo_dribbble = _TDIconsData(0xE4A8, 'logo_dribbble'); + static const logo_facebook_filled = _TDIconsData(0xE4A9, 'logo_facebook_filled'); + static const logo_facebook = _TDIconsData(0xE4AA, 'logo_facebook'); + static const logo_figma_filled = _TDIconsData(0xE4AB, 'logo_figma_filled'); + static const logo_figma = _TDIconsData(0xE4AC, 'logo_figma'); + static const logo_framer_filled = _TDIconsData(0xE4AD, 'logo_framer_filled'); + static const logo_framer = _TDIconsData(0xE4AE, 'logo_framer'); + static const logo_github_filled = _TDIconsData(0xE4AF, 'logo_github_filled'); + static const logo_github = _TDIconsData(0xE4B0, 'logo_github'); + static const logo_gitlab_filled = _TDIconsData(0xE4B1, 'logo_gitlab_filled'); + static const logo_gitlab = _TDIconsData(0xE4B2, 'logo_gitlab'); + static const logo_ie_filled = _TDIconsData(0xE4B3, 'logo_ie_filled'); + static const logo_ie = _TDIconsData(0xE4B4, 'logo_ie'); + static const logo_instagram_filled = _TDIconsData(0xE4B5, 'logo_instagram_filled'); + static const logo_instagram = _TDIconsData(0xE4B6, 'logo_instagram'); + static const logo_qq_filled = _TDIconsData(0xE4B7, 'logo_qq_filled'); + static const logo_qq = _TDIconsData(0xE4B8, 'logo_qq'); + static const logo_twitter_filled = _TDIconsData(0xE4B9, 'logo_twitter_filled'); + static const logo_twitter = _TDIconsData(0xE4BA, 'logo_twitter'); + static const logo_wechat_stroke_filled = _TDIconsData(0xE4BB, 'logo_wechat_stroke_filled'); + static const logo_wechat_stroke = _TDIconsData(0xE4BC, 'logo_wechat_stroke'); + static const logo_wechatpay_filled = _TDIconsData(0xE4BD, 'logo_wechatpay_filled'); + static const logo_wechatpay = _TDIconsData(0xE4BE, 'logo_wechatpay'); + static const logo_wecom_filled = _TDIconsData(0xE4BF, 'logo_wecom_filled'); + static const logo_wecom = _TDIconsData(0xE4C0, 'logo_wecom'); + static const logo_windows_filled = _TDIconsData(0xE4C1, 'logo_windows_filled'); + static const logo_windows = _TDIconsData(0xE4C2, 'logo_windows'); + static const logo_youtube_filled = _TDIconsData(0xE4C3, 'logo_youtube_filled'); + static const logo_youtube = _TDIconsData(0xE4C4, 'logo_youtube'); + static const logout = _TDIconsData(0xE4C5, 'logout'); + static const look_around_filled = _TDIconsData(0xE4C6, 'look_around_filled'); + static const look_around = _TDIconsData(0xE4C7, 'look_around'); + static const loudspeaker_filled = _TDIconsData(0xE4C8, 'loudspeaker_filled'); + static const loudspeaker = _TDIconsData(0xE4C9, 'loudspeaker'); + static const mail_filled = _TDIconsData(0xE4CA, 'mail_filled'); + static const mail = _TDIconsData(0xE4CB, 'mail'); + static const map_3d_filled = _TDIconsData(0xE4CC, 'map_3d_filled'); + static const map_3d = _TDIconsData(0xE4CD, 'map_3d'); + static const map_add_filled = _TDIconsData(0xE4CE, 'map_add_filled'); + static const map_add = _TDIconsData(0xE4CF, 'map_add'); + static const map_aiming_filled = _TDIconsData(0xE4D0, 'map_aiming_filled'); + static const map_aiming = _TDIconsData(0xE4D1, 'map_aiming'); + static const map_blocked_filled = _TDIconsData(0xE4D2, 'map_blocked_filled'); + static const map_blocked = _TDIconsData(0xE4D3, 'map_blocked'); + static const map_bubble_filled = _TDIconsData(0xE4D4, 'map_bubble_filled'); + static const map_bubble = _TDIconsData(0xE4D5, 'map_bubble'); + static const map_cancel_filled = _TDIconsData(0xE4D6, 'map_cancel_filled'); + static const map_cancel = _TDIconsData(0xE4D7, 'map_cancel'); + static const map_chat_filled = _TDIconsData(0xE4D8, 'map_chat_filled'); + static const map_chat = _TDIconsData(0xE4D9, 'map_chat'); + static const map_checked_filled = _TDIconsData(0xE4DA, 'map_checked_filled'); + static const map_checked = _TDIconsData(0xE4DB, 'map_checked'); + static const map_collection_filled = _TDIconsData(0xE4DC, 'map_collection_filled'); + static const map_collection = _TDIconsData(0xE4DD, 'map_collection'); + static const map_connection_filled = _TDIconsData(0xE4DE, 'map_connection_filled'); + static const map_connection = _TDIconsData(0xE4DF, 'map_connection'); + static const map_distance_filled = _TDIconsData(0xE4E0, 'map_distance_filled'); + static const map_distance = _TDIconsData(0xE4E1, 'map_distance'); + static const map_double_filled = _TDIconsData(0xE4E2, 'map_double_filled'); + static const map_double = _TDIconsData(0xE4E3, 'map_double'); + static const map_edit_filled = _TDIconsData(0xE4E4, 'map_edit_filled'); + static const map_edit = _TDIconsData(0xE4E5, 'map_edit'); + static const map_filled = _TDIconsData(0xE4E6, 'map_filled'); + static const map_grid_filled = _TDIconsData(0xE4E7, 'map_grid_filled'); + static const map_grid = _TDIconsData(0xE4E8, 'map_grid'); + static const map_information_1_filled = _TDIconsData(0xE4E9, 'map_information_1_filled'); + static const map_information_1 = _TDIconsData(0xE4EA, 'map_information_1'); + static const map_information_2_filled = _TDIconsData(0xE4EB, 'map_information_2_filled'); + static const map_information_2 = _TDIconsData(0xE4EC, 'map_information_2'); + static const map_information_filled = _TDIconsData(0xE4ED, 'map_information_filled'); + static const map_information = _TDIconsData(0xE4EE, 'map_information'); + static const map_location_filled = _TDIconsData(0xE4EF, 'map_location_filled'); + static const map_location = _TDIconsData(0xE4F0, 'map_location'); + static const map_locked_filled = _TDIconsData(0xE4F1, 'map_locked_filled'); + static const map_locked = _TDIconsData(0xE4F2, 'map_locked'); + static const map_marked_filled = _TDIconsData(0xE4F3, 'map_marked_filled'); + static const map_marked = _TDIconsData(0xE4F4, 'map_marked'); + static const map_navigation_filled = _TDIconsData(0xE4F5, 'map_navigation_filled'); + static const map_navigation = _TDIconsData(0xE4F6, 'map_navigation'); + static const map_outline_filled = _TDIconsData(0xE4F7, 'map_outline_filled'); + static const map_outline = _TDIconsData(0xE4F8, 'map_outline'); + static const map_route_planning_filled = _TDIconsData(0xE4F9, 'map_route_planning_filled'); + static const map_route_planning = _TDIconsData(0xE4FA, 'map_route_planning'); + static const map_ruler_filled = _TDIconsData(0xE4FB, 'map_ruler_filled'); + static const map_ruler = _TDIconsData(0xE4FC, 'map_ruler'); + static const map_safety_filled = _TDIconsData(0xE4FD, 'map_safety_filled'); + static const map_safety = _TDIconsData(0xE4FE, 'map_safety'); + static const map_search_1_filled = _TDIconsData(0xE4FF, 'map_search_1_filled'); + static const map_search_1 = _TDIconsData(0xE500, 'map_search_1'); + static const map_search_filled = _TDIconsData(0xE501, 'map_search_filled'); + static const map_search = _TDIconsData(0xE502, 'map_search'); + static const map_setting_filled = _TDIconsData(0xE503, 'map_setting_filled'); + static const map_setting = _TDIconsData(0xE504, 'map_setting'); + static const map_unlocked_filled = _TDIconsData(0xE505, 'map_unlocked_filled'); + static const map_unlocked = _TDIconsData(0xE506, 'map_unlocked'); + static const map = _TDIconsData(0xE507, 'map'); + static const mark_as_unread_filled = _TDIconsData(0xE508, 'mark_as_unread_filled'); + static const mark_as_unread = _TDIconsData(0xE509, 'mark_as_unread'); + static const markup_filled = _TDIconsData(0xE50A, 'markup_filled'); + static const markup = _TDIconsData(0xE50B, 'markup'); + static const mathematics_filled = _TDIconsData(0xE50C, 'mathematics_filled'); + static const mathematics = _TDIconsData(0xE50D, 'mathematics'); + static const measurement_1_filled = _TDIconsData(0xE50E, 'measurement_1_filled'); + static const measurement_1 = _TDIconsData(0xE50F, 'measurement_1'); + static const measurement_2_filled = _TDIconsData(0xE510, 'measurement_2_filled'); + static const measurement_2 = _TDIconsData(0xE511, 'measurement_2'); + static const measurement_filled = _TDIconsData(0xE512, 'measurement_filled'); + static const measurement = _TDIconsData(0xE513, 'measurement'); + static const meat_pepper_filled = _TDIconsData(0xE514, 'meat_pepper_filled'); + static const meat_pepper = _TDIconsData(0xE515, 'meat_pepper'); + static const media_library_filled = _TDIconsData(0xE516, 'media_library_filled'); + static const media_library = _TDIconsData(0xE517, 'media_library'); + static const member_filled = _TDIconsData(0xE518, 'member_filled'); + static const member = _TDIconsData(0xE519, 'member'); + static const menu_application = _TDIconsData(0xE51A, 'menu_application'); + static const menu_filled = _TDIconsData(0xE51B, 'menu_filled'); + static const menu_fold = _TDIconsData(0xE51C, 'menu_fold'); + static const menu_unfold = _TDIconsData(0xE51D, 'menu_unfold'); + static const menu = _TDIconsData(0xE51E, 'menu'); + static const merge_cells_filled = _TDIconsData(0xE51F, 'merge_cells_filled'); + static const merge_cells = _TDIconsData(0xE520, 'merge_cells'); + static const microphone_1_filled = _TDIconsData(0xE521, 'microphone_1_filled'); + static const microphone_1 = _TDIconsData(0xE522, 'microphone_1'); + static const microphone_2_filled = _TDIconsData(0xE523, 'microphone_2_filled'); + static const microphone_2 = _TDIconsData(0xE524, 'microphone_2'); + static const microphone_filled = _TDIconsData(0xE525, 'microphone_filled'); + static const microphone = _TDIconsData(0xE526, 'microphone'); + static const milk_filled = _TDIconsData(0xE527, 'milk_filled'); + static const milk = _TDIconsData(0xE528, 'milk'); + static const minus_circle_filled = _TDIconsData(0xE529, 'minus_circle_filled'); + static const minus_circle = _TDIconsData(0xE52A, 'minus_circle'); + static const minus_rectangle_filled = _TDIconsData(0xE52B, 'minus_rectangle_filled'); + static const minus_rectangle = _TDIconsData(0xE52C, 'minus_rectangle'); + static const minus = _TDIconsData(0xE52D, 'minus'); + static const mirror_filled = _TDIconsData(0xE52E, 'mirror_filled'); + static const mirror = _TDIconsData(0xE52F, 'mirror'); + static const mobile_blocked_filled = _TDIconsData(0xE530, 'mobile_blocked_filled'); + static const mobile_blocked = _TDIconsData(0xE531, 'mobile_blocked'); + static const mobile_filled = _TDIconsData(0xE532, 'mobile_filled'); + static const mobile_list_filled = _TDIconsData(0xE533, 'mobile_list_filled'); + static const mobile_list = _TDIconsData(0xE534, 'mobile_list'); + static const mobile_navigation_filled = _TDIconsData(0xE535, 'mobile_navigation_filled'); + static const mobile_navigation = _TDIconsData(0xE536, 'mobile_navigation'); + static const mobile_shortcut_filled = _TDIconsData(0xE537, 'mobile_shortcut_filled'); + static const mobile_shortcut = _TDIconsData(0xE538, 'mobile_shortcut'); + static const mobile_vibrate_filled = _TDIconsData(0xE539, 'mobile_vibrate_filled'); + static const mobile_vibrate = _TDIconsData(0xE53A, 'mobile_vibrate'); + static const mobile = _TDIconsData(0xE53B, 'mobile'); + static const mode_dark_filled = _TDIconsData(0xE53C, 'mode_dark_filled'); + static const mode_dark = _TDIconsData(0xE53D, 'mode_dark'); + static const mode_light_filled = _TDIconsData(0xE53E, 'mode_light_filled'); + static const mode_light = _TDIconsData(0xE53F, 'mode_light'); + static const module_filled = _TDIconsData(0xE540, 'module_filled'); + static const module = _TDIconsData(0xE541, 'module'); + static const money_filled = _TDIconsData(0xE542, 'money_filled'); + static const money = _TDIconsData(0xE543, 'money'); + static const monument_filled = _TDIconsData(0xE544, 'monument_filled'); + static const monument = _TDIconsData(0xE545, 'monument'); + static const moon_fall_filled = _TDIconsData(0xE546, 'moon_fall_filled'); + static const moon_fall = _TDIconsData(0xE547, 'moon_fall'); + static const moon_filled = _TDIconsData(0xE548, 'moon_filled'); + static const moon_rising_filled = _TDIconsData(0xE549, 'moon_rising_filled'); + static const moon_rising = _TDIconsData(0xE54A, 'moon_rising'); + static const moon = _TDIconsData(0xE54B, 'moon'); + static const more = _TDIconsData(0xE54C, 'more'); + static const mosque_1_filled = _TDIconsData(0xE54D, 'mosque_1_filled'); + static const mosque_1 = _TDIconsData(0xE54E, 'mosque_1'); + static const mosque_filled = _TDIconsData(0xE54F, 'mosque_filled'); + static const mosque = _TDIconsData(0xE550, 'mosque'); + static const mouse_filled = _TDIconsData(0xE551, 'mouse_filled'); + static const mouse = _TDIconsData(0xE552, 'mouse'); + static const move_1 = _TDIconsData(0xE553, 'move_1'); + static const move = _TDIconsData(0xE554, 'move'); + static const movie_clapper_filled = _TDIconsData(0xE555, 'movie_clapper_filled'); + static const movie_clapper = _TDIconsData(0xE556, 'movie_clapper'); + static const multiply = _TDIconsData(0xE557, 'multiply'); + static const museum_1_filled = _TDIconsData(0xE558, 'museum_1_filled'); + static const museum_1 = _TDIconsData(0xE559, 'museum_1'); + static const museum_2_filled = _TDIconsData(0xE55A, 'museum_2_filled'); + static const museum_2 = _TDIconsData(0xE55B, 'museum_2'); + static const museum_filled = _TDIconsData(0xE55C, 'museum_filled'); + static const museum = _TDIconsData(0xE55D, 'museum'); + static const mushroom_1_filled = _TDIconsData(0xE55E, 'mushroom_1_filled'); + static const mushroom_1 = _TDIconsData(0xE55F, 'mushroom_1'); + static const mushroom_filled = _TDIconsData(0xE560, 'mushroom_filled'); + static const mushroom = _TDIconsData(0xE561, 'mushroom'); + static const music_1_filled = _TDIconsData(0xE562, 'music_1_filled'); + static const music_1 = _TDIconsData(0xE563, 'music_1'); + static const music_2_filled = _TDIconsData(0xE564, 'music_2_filled'); + static const music_2 = _TDIconsData(0xE565, 'music_2'); + static const music_filled = _TDIconsData(0xE566, 'music_filled'); + static const music_rectangle_add_filled = _TDIconsData(0xE567, 'music_rectangle_add_filled'); + static const music_rectangle_add = _TDIconsData(0xE568, 'music_rectangle_add'); + static const music = _TDIconsData(0xE569, 'music'); + static const navigation_arrow_filled = _TDIconsData(0xE56A, 'navigation_arrow_filled'); + static const navigation_arrow = _TDIconsData(0xE56B, 'navigation_arrow'); + static const next_filled = _TDIconsData(0xE56C, 'next_filled'); + static const next = _TDIconsData(0xE56D, 'next'); + static const no_expression_filled = _TDIconsData(0xE56E, 'no_expression_filled'); + static const no_expression = _TDIconsData(0xE56F, 'no_expression'); + static const noodle_filled = _TDIconsData(0xE570, 'noodle_filled'); + static const noodle = _TDIconsData(0xE571, 'noodle'); + static const notification_add_filled = _TDIconsData(0xE572, 'notification_add_filled'); + static const notification_add = _TDIconsData(0xE573, 'notification_add'); + static const notification_circle_filled = _TDIconsData(0xE574, 'notification_circle_filled'); + static const notification_circle = _TDIconsData(0xE575, 'notification_circle'); + static const notification_error_filled = _TDIconsData(0xE576, 'notification_error_filled'); + static const notification_error = _TDIconsData(0xE577, 'notification_error'); + static const notification_filled = _TDIconsData(0xE578, 'notification_filled'); + static const notification = _TDIconsData(0xE579, 'notification'); + static const numbers_0_1 = _TDIconsData(0xE57A, 'numbers_0_1'); + static const numbers_0 = _TDIconsData(0xE57B, 'numbers_0'); + static const numbers_1_1 = _TDIconsData(0xE57C, 'numbers_1_1'); + static const numbers_1 = _TDIconsData(0xE57D, 'numbers_1'); + static const numbers_2_1 = _TDIconsData(0xE57E, 'numbers_2_1'); + static const numbers_2 = _TDIconsData(0xE57F, 'numbers_2'); + static const numbers_3_1 = _TDIconsData(0xE580, 'numbers_3_1'); + static const numbers_3 = _TDIconsData(0xE581, 'numbers_3'); + static const numbers_4_1 = _TDIconsData(0xE582, 'numbers_4_1'); + static const numbers_4 = _TDIconsData(0xE583, 'numbers_4'); + static const numbers_5_1 = _TDIconsData(0xE584, 'numbers_5_1'); + static const numbers_5 = _TDIconsData(0xE585, 'numbers_5'); + static const numbers_6_1 = _TDIconsData(0xE586, 'numbers_6_1'); + static const numbers_6 = _TDIconsData(0xE587, 'numbers_6'); + static const numbers_7_1 = _TDIconsData(0xE588, 'numbers_7_1'); + static const numbers_7 = _TDIconsData(0xE589, 'numbers_7'); + static const numbers_8_1 = _TDIconsData(0xE58A, 'numbers_8_1'); + static const numbers_8 = _TDIconsData(0xE58B, 'numbers_8'); + static const numbers_9_1 = _TDIconsData(0xE58C, 'numbers_9_1'); + static const numbers_9 = _TDIconsData(0xE58D, 'numbers_9'); + static const nut_filled = _TDIconsData(0xE58E, 'nut_filled'); + static const nut = _TDIconsData(0xE58F, 'nut'); + static const object_storage = _TDIconsData(0xE590, 'object_storage'); + static const open_mouth_filled = _TDIconsData(0xE591, 'open_mouth_filled'); + static const open_mouth = _TDIconsData(0xE592, 'open_mouth'); + static const opera_filled = _TDIconsData(0xE593, 'opera_filled'); + static const opera = _TDIconsData(0xE594, 'opera'); + static const order_adjustment_column = _TDIconsData(0xE595, 'order_adjustment_column'); + static const order_ascending = _TDIconsData(0xE596, 'order_ascending'); + static const order_descending = _TDIconsData(0xE597, 'order_descending'); + static const outbox_filled = _TDIconsData(0xE598, 'outbox_filled'); + static const outbox = _TDIconsData(0xE599, 'outbox'); + static const page_first = _TDIconsData(0xE59A, 'page_first'); + static const page_head_filled = _TDIconsData(0xE59B, 'page_head_filled'); + static const page_head = _TDIconsData(0xE59C, 'page_head'); + static const page_last = _TDIconsData(0xE59D, 'page_last'); + static const palace_1_filled = _TDIconsData(0xE59E, 'palace_1_filled'); + static const palace_1 = _TDIconsData(0xE59F, 'palace_1'); + static const palace_2_filled = _TDIconsData(0xE5A0, 'palace_2_filled'); + static const palace_2 = _TDIconsData(0xE5A1, 'palace_2'); + static const palace_3_filled = _TDIconsData(0xE5A2, 'palace_3_filled'); + static const palace_3 = _TDIconsData(0xE5A3, 'palace_3'); + static const palace_4_filled = _TDIconsData(0xE5A4, 'palace_4_filled'); + static const palace_4 = _TDIconsData(0xE5A5, 'palace_4'); + static const palace_filled = _TDIconsData(0xE5A6, 'palace_filled'); + static const palace = _TDIconsData(0xE5A7, 'palace'); + static const palette_1_filled = _TDIconsData(0xE5A8, 'palette_1_filled'); + static const palette_1 = _TDIconsData(0xE5A9, 'palette_1'); + static const palette_filled = _TDIconsData(0xE5AA, 'palette_filled'); + static const palette = _TDIconsData(0xE5AB, 'palette'); + static const panorama_horizontal_filled = _TDIconsData(0xE5AC, 'panorama_horizontal_filled'); + static const panorama_horizontal = _TDIconsData(0xE5AD, 'panorama_horizontal'); + static const panorama_vertical_filled = _TDIconsData(0xE5AE, 'panorama_vertical_filled'); + static const panorama_vertical = _TDIconsData(0xE5AF, 'panorama_vertical'); + static const pantone_filled = _TDIconsData(0xE5B0, 'pantone_filled'); + static const pantone = _TDIconsData(0xE5B1, 'pantone'); + static const parabola = _TDIconsData(0xE5B2, 'parabola'); + static const parentheses = _TDIconsData(0xE5B3, 'parentheses'); + static const paste_filled = _TDIconsData(0xE5B4, 'paste_filled'); + static const paste = _TDIconsData(0xE5B5, 'paste'); + static const patio_filled = _TDIconsData(0xE5B6, 'patio_filled'); + static const patio = _TDIconsData(0xE5B7, 'patio'); + static const pause_circle_filled = _TDIconsData(0xE5B8, 'pause_circle_filled'); + static const pause_circle_stroke_filled = _TDIconsData(0xE5B9, 'pause_circle_stroke_filled'); + static const pause_circle_stroke = _TDIconsData(0xE5BA, 'pause_circle_stroke'); + static const pause_circle = _TDIconsData(0xE5BB, 'pause_circle'); + static const pause = _TDIconsData(0xE5BC, 'pause'); + static const pea_filled = _TDIconsData(0xE5BD, 'pea_filled'); + static const pea = _TDIconsData(0xE5BE, 'pea'); + static const peach_filled = _TDIconsData(0xE5BF, 'peach_filled'); + static const peach = _TDIconsData(0xE5C0, 'peach'); + static const pear_filled = _TDIconsData(0xE5C1, 'pear_filled'); + static const pear = _TDIconsData(0xE5C2, 'pear'); + static const pearl_of_the_orient_filled = _TDIconsData(0xE5C3, 'pearl_of_the_orient_filled'); + static const pearl_of_the_orient = _TDIconsData(0xE5C4, 'pearl_of_the_orient'); + static const pen_ball_filled = _TDIconsData(0xE5C5, 'pen_ball_filled'); + static const pen_ball = _TDIconsData(0xE5C6, 'pen_ball'); + static const pen_brush_filled = _TDIconsData(0xE5C7, 'pen_brush_filled'); + static const pen_brush = _TDIconsData(0xE5C8, 'pen_brush'); + static const pen_filled = _TDIconsData(0xE5C9, 'pen_filled'); + static const pen_mark_filled = _TDIconsData(0xE5CA, 'pen_mark_filled'); + static const pen_mark = _TDIconsData(0xE5CB, 'pen_mark'); + static const pen_quill_filled = _TDIconsData(0xE5CC, 'pen_quill_filled'); + static const pen_quill = _TDIconsData(0xE5CD, 'pen_quill'); + static const pen = _TDIconsData(0xE5CE, 'pen'); + static const pending_filled = _TDIconsData(0xE5CF, 'pending_filled'); + static const pending = _TDIconsData(0xE5D0, 'pending'); + static const percent = _TDIconsData(0xE5D1, 'percent'); + static const personal_information_filled = _TDIconsData(0xE5D2, 'personal_information_filled'); + static const personal_information = _TDIconsData(0xE5D3, 'personal_information'); + static const phone_locked_filled = _TDIconsData(0xE5D4, 'phone_locked_filled'); + static const phone_locked = _TDIconsData(0xE5D5, 'phone_locked'); + static const phone_search_filled = _TDIconsData(0xE5D6, 'phone_search_filled'); + static const phone_search = _TDIconsData(0xE5D7, 'phone_search'); + static const pi = _TDIconsData(0xE5D8, 'pi'); + static const piano_filled = _TDIconsData(0xE5D9, 'piano_filled'); + static const piano = _TDIconsData(0xE5DA, 'piano'); + static const pin_filled = _TDIconsData(0xE5DB, 'pin_filled'); + static const pin = _TDIconsData(0xE5DC, 'pin'); + static const play_circle_filled = _TDIconsData(0xE5DD, 'play_circle_filled'); + static const play_circle_stroke_add_filled = _TDIconsData(0xE5DE, 'play_circle_stroke_add_filled'); + static const play_circle_stroke_add = _TDIconsData(0xE5DF, 'play_circle_stroke_add'); + static const play_circle_stroke_filled = _TDIconsData(0xE5E0, 'play_circle_stroke_filled'); + static const play_circle_stroke = _TDIconsData(0xE5E1, 'play_circle_stroke'); + static const play_circle = _TDIconsData(0xE5E2, 'play_circle'); + static const play_demo_filled = _TDIconsData(0xE5E3, 'play_demo_filled'); + static const play_demo = _TDIconsData(0xE5E4, 'play_demo'); + static const play_rectangle_filled = _TDIconsData(0xE5E5, 'play_rectangle_filled'); + static const play_rectangle = _TDIconsData(0xE5E6, 'play_rectangle'); + static const play = _TDIconsData(0xE5E7, 'play'); + static const plus = _TDIconsData(0xE5E8, 'plus'); + static const popsicle_filled = _TDIconsData(0xE5E9, 'popsicle_filled'); + static const popsicle = _TDIconsData(0xE5EA, 'popsicle'); + static const portrait_filled = _TDIconsData(0xE5EB, 'portrait_filled'); + static const portrait = _TDIconsData(0xE5EC, 'portrait'); + static const pout_filled = _TDIconsData(0xE5ED, 'pout_filled'); + static const pout = _TDIconsData(0xE5EE, 'pout'); + static const poweroff = _TDIconsData(0xE5EF, 'poweroff'); + static const precise_monitor = _TDIconsData(0xE5F0, 'precise_monitor'); + static const previous_filled = _TDIconsData(0xE5F1, 'previous_filled'); + static const previous = _TDIconsData(0xE5F2, 'previous'); + static const print_filled = _TDIconsData(0xE5F3, 'print_filled'); + static const print = _TDIconsData(0xE5F4, 'print'); + static const pumpkin_filled = _TDIconsData(0xE5F5, 'pumpkin_filled'); + static const pumpkin = _TDIconsData(0xE5F6, 'pumpkin'); + static const pyramid_filled = _TDIconsData(0xE5F7, 'pyramid_filled'); + static const pyramid_maya_filled = _TDIconsData(0xE5F8, 'pyramid_maya_filled'); + static const pyramid_maya = _TDIconsData(0xE5F9, 'pyramid_maya'); + static const pyramid = _TDIconsData(0xE5FA, 'pyramid'); + static const qrcode = _TDIconsData(0xE5FB, 'qrcode'); + static const quadratic = _TDIconsData(0xE5FC, 'quadratic'); + static const questionnaire_double_filled = _TDIconsData(0xE5FD, 'questionnaire_double_filled'); + static const questionnaire_double = _TDIconsData(0xE5FE, 'questionnaire_double'); + static const questionnaire_filled = _TDIconsData(0xE5FF, 'questionnaire_filled'); + static const questionnaire = _TDIconsData(0xE600, 'questionnaire'); + static const queue_filled = _TDIconsData(0xE601, 'queue_filled'); + static const queue = _TDIconsData(0xE602, 'queue'); + static const radar = _TDIconsData(0xE603, 'radar'); + static const radio_1_filled = _TDIconsData(0xE604, 'radio_1_filled'); + static const radio_1 = _TDIconsData(0xE605, 'radio_1'); + static const radio_2_filled = _TDIconsData(0xE606, 'radio_2_filled'); + static const radio_2 = _TDIconsData(0xE607, 'radio_2'); + static const radish_filled = _TDIconsData(0xE608, 'radish_filled'); + static const radish = _TDIconsData(0xE609, 'radish'); + static const rain_heavy = _TDIconsData(0xE60A, 'rain_heavy'); + static const rain_light_filled = _TDIconsData(0xE60B, 'rain_light_filled'); + static const rain_light = _TDIconsData(0xE60C, 'rain_light'); + static const rain_medium = _TDIconsData(0xE60D, 'rain_medium'); + static const rainbow = _TDIconsData(0xE60E, 'rainbow'); + static const rectangle_filled = _TDIconsData(0xE60F, 'rectangle_filled'); + static const rectangle = _TDIconsData(0xE610, 'rectangle'); + static const refresh = _TDIconsData(0xE611, 'refresh'); + static const relation = _TDIconsData(0xE612, 'relation'); + static const relativity_filled = _TDIconsData(0xE613, 'relativity_filled'); + static const relativity = _TDIconsData(0xE614, 'relativity'); + static const remote_wave_filled = _TDIconsData(0xE615, 'remote_wave_filled'); + static const remote_wave = _TDIconsData(0xE616, 'remote_wave'); + static const remove = _TDIconsData(0xE617, 'remove'); + static const replay_filled = _TDIconsData(0xE618, 'replay_filled'); + static const replay = _TDIconsData(0xE619, 'replay'); + static const rice_ball_filled = _TDIconsData(0xE61A, 'rice_ball_filled'); + static const rice_ball = _TDIconsData(0xE61B, 'rice_ball'); + static const rice_filled = _TDIconsData(0xE61C, 'rice_filled'); + static const rice = _TDIconsData(0xE61D, 'rice'); + static const roast_filled = _TDIconsData(0xE61E, 'roast_filled'); + static const roast = _TDIconsData(0xE61F, 'roast'); + static const rocket_filled = _TDIconsData(0xE620, 'rocket_filled'); + static const rocket = _TDIconsData(0xE621, 'rocket'); + static const rollback = _TDIconsData(0xE622, 'rollback'); + static const rollfront = _TDIconsData(0xE623, 'rollfront'); + static const root_list_filled = _TDIconsData(0xE624, 'root_list_filled'); + static const root_list = _TDIconsData(0xE625, 'root_list'); + static const rotate_locked_filled = _TDIconsData(0xE626, 'rotate_locked_filled'); + static const rotate_locked = _TDIconsData(0xE627, 'rotate_locked'); + static const rotate = _TDIconsData(0xE628, 'rotate'); + static const rotation = _TDIconsData(0xE629, 'rotation'); + static const round_filled = _TDIconsData(0xE62A, 'round_filled'); + static const round = _TDIconsData(0xE62B, 'round'); + static const router_wave_filled = _TDIconsData(0xE62C, 'router_wave_filled'); + static const router_wave = _TDIconsData(0xE62D, 'router_wave'); + static const rss = _TDIconsData(0xE62E, 'rss'); + static const ruler_filled = _TDIconsData(0xE62F, 'ruler_filled'); + static const ruler = _TDIconsData(0xE630, 'ruler'); + static const sailing_hotel_filled = _TDIconsData(0xE631, 'sailing_hotel_filled'); + static const sailing_hotel = _TDIconsData(0xE632, 'sailing_hotel'); + static const sandwich_filled = _TDIconsData(0xE633, 'sandwich_filled'); + static const sandwich = _TDIconsData(0xE634, 'sandwich'); + static const saturation_filled = _TDIconsData(0xE635, 'saturation_filled'); + static const saturation = _TDIconsData(0xE636, 'saturation'); + static const sausage_filled = _TDIconsData(0xE637, 'sausage_filled'); + static const sausage = _TDIconsData(0xE638, 'sausage'); + static const save_filled = _TDIconsData(0xE639, 'save_filled'); + static const save = _TDIconsData(0xE63A, 'save'); + static const saving_pot_filled = _TDIconsData(0xE63B, 'saving_pot_filled'); + static const saving_pot = _TDIconsData(0xE63C, 'saving_pot'); + static const scan = _TDIconsData(0xE63D, 'scan'); + static const screen_4k_filled = _TDIconsData(0xE63E, 'screen_4k_filled'); + static const screen_4k = _TDIconsData(0xE63F, 'screen_4k'); + static const screencast_filled = _TDIconsData(0xE640, 'screencast_filled'); + static const screencast = _TDIconsData(0xE641, 'screencast'); + static const screenshot = _TDIconsData(0xE642, 'screenshot'); + static const scroll_bar_filled = _TDIconsData(0xE643, 'scroll_bar_filled'); + static const scroll_bar = _TDIconsData(0xE644, 'scroll_bar'); + static const sd_card_1_filled = _TDIconsData(0xE645, 'sd_card_1_filled'); + static const sd_card_1 = _TDIconsData(0xE646, 'sd_card_1'); + static const sd_card_filled = _TDIconsData(0xE647, 'sd_card_filled'); + static const sd_card = _TDIconsData(0xE648, 'sd_card'); + static const search_error_filled = _TDIconsData(0xE649, 'search_error_filled'); + static const search_error = _TDIconsData(0xE64A, 'search_error'); + static const search_filled = _TDIconsData(0xE64B, 'search_filled'); + static const search = _TDIconsData(0xE64C, 'search'); + static const secured_filled = _TDIconsData(0xE64D, 'secured_filled'); + static const secured = _TDIconsData(0xE64E, 'secured'); + static const send_cancel_filled = _TDIconsData(0xE64F, 'send_cancel_filled'); + static const send_cancel = _TDIconsData(0xE650, 'send_cancel'); + static const send_filled = _TDIconsData(0xE651, 'send_filled'); + static const send = _TDIconsData(0xE652, 'send'); + static const sensors_1 = _TDIconsData(0xE653, 'sensors_1'); + static const sensors_2 = _TDIconsData(0xE654, 'sensors_2'); + static const sensors_off = _TDIconsData(0xE655, 'sensors_off'); + static const sensors = _TDIconsData(0xE656, 'sensors'); + static const sequence_filled = _TDIconsData(0xE657, 'sequence_filled'); + static const sequence = _TDIconsData(0xE658, 'sequence'); + static const serenity_filled = _TDIconsData(0xE659, 'serenity_filled'); + static const serenity = _TDIconsData(0xE65A, 'serenity'); + static const server_filled = _TDIconsData(0xE65B, 'server_filled'); + static const server = _TDIconsData(0xE65C, 'server'); + static const service_filled = _TDIconsData(0xE65D, 'service_filled'); + static const service = _TDIconsData(0xE65E, 'service'); + static const setting_1_filled = _TDIconsData(0xE65F, 'setting_1_filled'); + static const setting_1 = _TDIconsData(0xE660, 'setting_1'); + static const setting_filled = _TDIconsData(0xE661, 'setting_filled'); + static const setting = _TDIconsData(0xE662, 'setting'); + static const share_1_filled = _TDIconsData(0xE663, 'share_1_filled'); + static const share_1 = _TDIconsData(0xE664, 'share_1'); + static const share_filled = _TDIconsData(0xE665, 'share_filled'); + static const share = _TDIconsData(0xE666, 'share'); + static const sharpness_filled = _TDIconsData(0xE667, 'sharpness_filled'); + static const sharpness = _TDIconsData(0xE668, 'sharpness'); + static const shield_error_filled = _TDIconsData(0xE669, 'shield_error_filled'); + static const shield_error = _TDIconsData(0xE66A, 'shield_error'); + static const shimen_filled = _TDIconsData(0xE66B, 'shimen_filled'); + static const shimen = _TDIconsData(0xE66C, 'shimen'); + static const shop_1_filled = _TDIconsData(0xE66D, 'shop_1_filled'); + static const shop_1 = _TDIconsData(0xE66E, 'shop_1'); + static const shop_2_filled = _TDIconsData(0xE66F, 'shop_2_filled'); + static const shop_2 = _TDIconsData(0xE670, 'shop_2'); + static const shop_3_filled = _TDIconsData(0xE671, 'shop_3_filled'); + static const shop_3 = _TDIconsData(0xE672, 'shop_3'); + static const shop_4_filled = _TDIconsData(0xE673, 'shop_4_filled'); + static const shop_4 = _TDIconsData(0xE674, 'shop_4'); + static const shop_5_filled = _TDIconsData(0xE675, 'shop_5_filled'); + static const shop_5 = _TDIconsData(0xE676, 'shop_5'); + static const shop_filled = _TDIconsData(0xE677, 'shop_filled'); + static const shop = _TDIconsData(0xE678, 'shop'); + static const shrimp_filled = _TDIconsData(0xE679, 'shrimp_filled'); + static const shrimp = _TDIconsData(0xE67A, 'shrimp'); + static const shrink_horizontal = _TDIconsData(0xE67B, 'shrink_horizontal'); + static const shrink_vertical = _TDIconsData(0xE67C, 'shrink_vertical'); + static const shutter_filled = _TDIconsData(0xE67D, 'shutter_filled'); + static const shutter = _TDIconsData(0xE67E, 'shutter'); + static const shutup_filled = _TDIconsData(0xE67F, 'shutup_filled'); + static const shutup = _TDIconsData(0xE680, 'shutup'); + static const sim_card_1_filled = _TDIconsData(0xE681, 'sim_card_1_filled'); + static const sim_card_1 = _TDIconsData(0xE682, 'sim_card_1'); + static const sim_card_2_filled = _TDIconsData(0xE683, 'sim_card_2_filled'); + static const sim_card_2 = _TDIconsData(0xE684, 'sim_card_2'); + static const sim_card_filled = _TDIconsData(0xE685, 'sim_card_filled'); + static const sim_card = _TDIconsData(0xE686, 'sim_card'); + static const sinister_smile_filled = _TDIconsData(0xE687, 'sinister_smile_filled'); + static const sinister_smile = _TDIconsData(0xE688, 'sinister_smile'); + static const sip_filled = _TDIconsData(0xE689, 'sip_filled'); + static const sip = _TDIconsData(0xE68A, 'sip'); + static const sitemap_filled = _TDIconsData(0xE68B, 'sitemap_filled'); + static const sitemap = _TDIconsData(0xE68C, 'sitemap'); + static const slash = _TDIconsData(0xE68D, 'slash'); + static const sleep_filled = _TDIconsData(0xE68E, 'sleep_filled'); + static const sleep = _TDIconsData(0xE68F, 'sleep'); + static const slice_filled = _TDIconsData(0xE690, 'slice_filled'); + static const slice = _TDIconsData(0xE691, 'slice'); + static const slideshow_filled = _TDIconsData(0xE692, 'slideshow_filled'); + static const slideshow = _TDIconsData(0xE693, 'slideshow'); + static const smile_filled = _TDIconsData(0xE694, 'smile_filled'); + static const smile = _TDIconsData(0xE695, 'smile'); + static const sneer_filled = _TDIconsData(0xE696, 'sneer_filled'); + static const sneer = _TDIconsData(0xE697, 'sneer'); + static const snowflake = _TDIconsData(0xE698, 'snowflake'); + static const sonic = _TDIconsData(0xE699, 'sonic'); + static const sound_down_filled = _TDIconsData(0xE69A, 'sound_down_filled'); + static const sound_down = _TDIconsData(0xE69B, 'sound_down'); + static const sound_filled = _TDIconsData(0xE69C, 'sound_filled'); + static const sound_high_filled = _TDIconsData(0xE69D, 'sound_high_filled'); + static const sound_high = _TDIconsData(0xE69E, 'sound_high'); + static const sound_low_filled = _TDIconsData(0xE69F, 'sound_low_filled'); + static const sound_low = _TDIconsData(0xE6A0, 'sound_low'); + static const sound_mute_1_filled = _TDIconsData(0xE6A1, 'sound_mute_1_filled'); + static const sound_mute_1 = _TDIconsData(0xE6A2, 'sound_mute_1'); + static const sound_mute_filled = _TDIconsData(0xE6A3, 'sound_mute_filled'); + static const sound_mute = _TDIconsData(0xE6A4, 'sound_mute'); + static const sound_up_filled = _TDIconsData(0xE6A5, 'sound_up_filled'); + static const sound_up = _TDIconsData(0xE6A6, 'sound_up'); + static const sound = _TDIconsData(0xE6A7, 'sound'); + static const space = _TDIconsData(0xE6A8, 'space'); + static const speechless_1_filled = _TDIconsData(0xE6A9, 'speechless_1_filled'); + static const speechless_1 = _TDIconsData(0xE6AA, 'speechless_1'); + static const speechless_filled = _TDIconsData(0xE6AB, 'speechless_filled'); + static const speechless = _TDIconsData(0xE6AC, 'speechless'); + static const star_filled = _TDIconsData(0xE6AD, 'star_filled'); + static const star = _TDIconsData(0xE6AE, 'star'); + static const statue_of_jesus_filled = _TDIconsData(0xE6AF, 'statue_of_jesus_filled'); + static const statue_of_jesus = _TDIconsData(0xE6B0, 'statue_of_jesus'); + static const sticky_note_filled = _TDIconsData(0xE6B1, 'sticky_note_filled'); + static const sticky_note = _TDIconsData(0xE6B2, 'sticky_note'); + static const stop_circle_filled = _TDIconsData(0xE6B3, 'stop_circle_filled'); + static const stop_circle_stroke_filled = _TDIconsData(0xE6B4, 'stop_circle_stroke_filled'); + static const stop_circle_stroke = _TDIconsData(0xE6B5, 'stop_circle_stroke'); + static const stop_circle = _TDIconsData(0xE6B6, 'stop_circle'); + static const stop = _TDIconsData(0xE6B7, 'stop'); + static const store_filled = _TDIconsData(0xE6B8, 'store_filled'); + static const store = _TDIconsData(0xE6B9, 'store'); + static const street_road_1_filled = _TDIconsData(0xE6BA, 'street_road_1_filled'); + static const street_road_1 = _TDIconsData(0xE6BB, 'street_road_1'); + static const street_road_filled = _TDIconsData(0xE6BC, 'street_road_filled'); + static const street_road = _TDIconsData(0xE6BD, 'street_road'); + static const subtitle_filled = _TDIconsData(0xE6BE, 'subtitle_filled'); + static const subtitle = _TDIconsData(0xE6BF, 'subtitle'); + static const subway_line_filled = _TDIconsData(0xE6C0, 'subway_line_filled'); + static const subway_line = _TDIconsData(0xE6C1, 'subway_line'); + static const sum = _TDIconsData(0xE6C2, 'sum'); + static const sun_fall_filled = _TDIconsData(0xE6C3, 'sun_fall_filled'); + static const sun_fall = _TDIconsData(0xE6C4, 'sun_fall'); + static const sun_rising_filled = _TDIconsData(0xE6C5, 'sun_rising_filled'); + static const sun_rising = _TDIconsData(0xE6C6, 'sun_rising'); + static const sunny_filled = _TDIconsData(0xE6C7, 'sunny_filled'); + static const sunny = _TDIconsData(0xE6C8, 'sunny'); + static const support_filled = _TDIconsData(0xE6C9, 'support_filled'); + static const support = _TDIconsData(0xE6CA, 'support'); + static const surprised_1_filled = _TDIconsData(0xE6CB, 'surprised_1_filled'); + static const surprised_1 = _TDIconsData(0xE6CC, 'surprised_1'); + static const surprised_filled = _TDIconsData(0xE6CD, 'surprised_filled'); + static const surprised = _TDIconsData(0xE6CE, 'surprised'); + static const swap_left = _TDIconsData(0xE6CF, 'swap_left'); + static const swap_right = _TDIconsData(0xE6D0, 'swap_right'); + static const swap = _TDIconsData(0xE6D1, 'swap'); + static const swear_1_filled = _TDIconsData(0xE6D2, 'swear_1_filled'); + static const swear_1 = _TDIconsData(0xE6D3, 'swear_1'); + static const swear_2_filled = _TDIconsData(0xE6D4, 'swear_2_filled'); + static const swear_2 = _TDIconsData(0xE6D5, 'swear_2'); + static const system_2 = _TDIconsData(0xE6D6, 'system_2'); + static const system_3_filled = _TDIconsData(0xE6D7, 'system_3_filled'); + static const system_3 = _TDIconsData(0xE6D8, 'system_3'); + static const system_application_filled = _TDIconsData(0xE6D9, 'system_application_filled'); + static const system_application = _TDIconsData(0xE6DA, 'system_application'); + static const system_blocked_filled = _TDIconsData(0xE6DB, 'system_blocked_filled'); + static const system_blocked = _TDIconsData(0xE6DC, 'system_blocked'); + static const system_code_filled = _TDIconsData(0xE6DD, 'system_code_filled'); + static const system_code = _TDIconsData(0xE6DE, 'system_code'); + static const system_components_filled = _TDIconsData(0xE6DF, 'system_components_filled'); + static const system_components = _TDIconsData(0xE6E0, 'system_components'); + static const system_coordinate_filled = _TDIconsData(0xE6E1, 'system_coordinate_filled'); + static const system_coordinate = _TDIconsData(0xE6E2, 'system_coordinate'); + static const system_device_filled = _TDIconsData(0xE6E3, 'system_device_filled'); + static const system_device = _TDIconsData(0xE6E4, 'system_device'); + static const system_interface_filled = _TDIconsData(0xE6E5, 'system_interface_filled'); + static const system_interface = _TDIconsData(0xE6E6, 'system_interface'); + static const system_location_filled = _TDIconsData(0xE6E7, 'system_location_filled'); + static const system_location = _TDIconsData(0xE6E8, 'system_location'); + static const system_locked_filled = _TDIconsData(0xE6E9, 'system_locked_filled'); + static const system_locked = _TDIconsData(0xE6EA, 'system_locked'); + static const system_log_filled = _TDIconsData(0xE6EB, 'system_log_filled'); + static const system_log = _TDIconsData(0xE6EC, 'system_log'); + static const system_marked_filled = _TDIconsData(0xE6ED, 'system_marked_filled'); + static const system_marked = _TDIconsData(0xE6EE, 'system_marked'); + static const system_messages_filled = _TDIconsData(0xE6EF, 'system_messages_filled'); + static const system_messages = _TDIconsData(0xE6F0, 'system_messages'); + static const system_regulation_filled = _TDIconsData(0xE6F1, 'system_regulation_filled'); + static const system_regulation = _TDIconsData(0xE6F2, 'system_regulation'); + static const system_search_filled = _TDIconsData(0xE6F3, 'system_search_filled'); + static const system_search = _TDIconsData(0xE6F4, 'system_search'); + static const system_setting_filled = _TDIconsData(0xE6F5, 'system_setting_filled'); + static const system_setting = _TDIconsData(0xE6F6, 'system_setting'); + static const system_storage_filled = _TDIconsData(0xE6F7, 'system_storage_filled'); + static const system_storage = _TDIconsData(0xE6F8, 'system_storage'); + static const system_sum = _TDIconsData(0xE6F9, 'system_sum'); + static const system_unlocked_filled = _TDIconsData(0xE6FA, 'system_unlocked_filled'); + static const system_unlocked = _TDIconsData(0xE6FB, 'system_unlocked'); + static const tab_filled = _TDIconsData(0xE6FC, 'tab_filled'); + static const tab = _TDIconsData(0xE6FD, 'tab'); + static const table_1_filled = _TDIconsData(0xE6FE, 'table_1_filled'); + static const table_1 = _TDIconsData(0xE6FF, 'table_1'); + static const table_2_filled = _TDIconsData(0xE700, 'table_2_filled'); + static const table_2 = _TDIconsData(0xE701, 'table_2'); + static const table_add_filled = _TDIconsData(0xE702, 'table_add_filled'); + static const table_add = _TDIconsData(0xE703, 'table_add'); + static const table_filled = _TDIconsData(0xE704, 'table_filled'); + static const table_split_filled = _TDIconsData(0xE705, 'table_split_filled'); + static const table_split = _TDIconsData(0xE706, 'table_split'); + static const table = _TDIconsData(0xE707, 'table'); + static const tag_filled = _TDIconsData(0xE708, 'tag_filled'); + static const tag = _TDIconsData(0xE709, 'tag'); + static const tangerinr_filled = _TDIconsData(0xE70A, 'tangerinr_filled'); + static const tangerinr = _TDIconsData(0xE70B, 'tangerinr'); + static const tape_filled = _TDIconsData(0xE70C, 'tape_filled'); + static const tape = _TDIconsData(0xE70D, 'tape'); + static const task_1_filled = _TDIconsData(0xE70E, 'task_1_filled'); + static const task_1 = _TDIconsData(0xE70F, 'task_1'); + static const task_add_1 = _TDIconsData(0xE710, 'task_add_1'); + static const task_add_filled = _TDIconsData(0xE711, 'task_add_filled'); + static const task_add = _TDIconsData(0xE712, 'task_add'); + static const task_checked_1 = _TDIconsData(0xE713, 'task_checked_1'); + static const task_checked_filled = _TDIconsData(0xE714, 'task_checked_filled'); + static const task_checked = _TDIconsData(0xE715, 'task_checked'); + static const task_double_filled = _TDIconsData(0xE716, 'task_double_filled'); + static const task_double = _TDIconsData(0xE717, 'task_double'); + static const task_error_filled = _TDIconsData(0xE718, 'task_error_filled'); + static const task_error = _TDIconsData(0xE719, 'task_error'); + static const task_filled = _TDIconsData(0xE71A, 'task_filled'); + static const task_location_filled = _TDIconsData(0xE71B, 'task_location_filled'); + static const task_location = _TDIconsData(0xE71C, 'task_location'); + static const task_marked_filled = _TDIconsData(0xE71D, 'task_marked_filled'); + static const task_marked = _TDIconsData(0xE71E, 'task_marked'); + static const task_setting_filled = _TDIconsData(0xE71F, 'task_setting_filled'); + static const task_setting = _TDIconsData(0xE720, 'task_setting'); + static const task_time_filled = _TDIconsData(0xE721, 'task_time_filled'); + static const task_time = _TDIconsData(0xE722, 'task_time'); + static const task_visible_filled = _TDIconsData(0xE723, 'task_visible_filled'); + static const task_visible = _TDIconsData(0xE724, 'task_visible'); + static const task = _TDIconsData(0xE725, 'task'); + static const tea_filled = _TDIconsData(0xE726, 'tea_filled'); + static const tea = _TDIconsData(0xE727, 'tea'); + static const teahouse_filled = _TDIconsData(0xE728, 'teahouse_filled'); + static const teahouse = _TDIconsData(0xE729, 'teahouse'); + static const template_filled = _TDIconsData(0xE72A, 'template_filled'); + static const template = _TDIconsData(0xE72B, 'template'); + static const temple_filled = _TDIconsData(0xE72C, 'temple_filled'); + static const temple = _TDIconsData(0xE72D, 'temple'); + static const terminal_rectangle_1_filled = _TDIconsData(0xE72E, 'terminal_rectangle_1_filled'); + static const terminal_rectangle_1 = _TDIconsData(0xE72F, 'terminal_rectangle_1'); + static const terminal_rectangle_filled = _TDIconsData(0xE730, 'terminal_rectangle_filled'); + static const terminal_rectangle = _TDIconsData(0xE731, 'terminal_rectangle'); + static const terminal_window_filled = _TDIconsData(0xE732, 'terminal_window_filled'); + static const terminal_window = _TDIconsData(0xE733, 'terminal_window'); + static const terminal = _TDIconsData(0xE734, 'terminal'); + static const textbox_filled = _TDIconsData(0xE735, 'textbox_filled'); + static const textbox = _TDIconsData(0xE736, 'textbox'); + static const textformat_bold = _TDIconsData(0xE737, 'textformat_bold'); + static const textformat_color = _TDIconsData(0xE738, 'textformat_color'); + static const textformat_italic = _TDIconsData(0xE739, 'textformat_italic'); + static const textformat_strikethrough = _TDIconsData(0xE73A, 'textformat_strikethrough'); + static const textformat_underline = _TDIconsData(0xE73B, 'textformat_underline'); + static const textformat_wrap = _TDIconsData(0xE73C, 'textformat_wrap'); + static const theaters_filled = _TDIconsData(0xE73D, 'theaters_filled'); + static const theaters = _TDIconsData(0xE73E, 'theaters'); + static const thumb_down_1_filled = _TDIconsData(0xE73F, 'thumb_down_1_filled'); + static const thumb_down_1 = _TDIconsData(0xE740, 'thumb_down_1'); + static const thumb_down_2_filled = _TDIconsData(0xE741, 'thumb_down_2_filled'); + static const thumb_down_2 = _TDIconsData(0xE742, 'thumb_down_2'); + static const thumb_down_filled = _TDIconsData(0xE743, 'thumb_down_filled'); + static const thumb_down = _TDIconsData(0xE744, 'thumb_down'); + static const thumb_up_1_filled = _TDIconsData(0xE745, 'thumb_up_1_filled'); + static const thumb_up_1 = _TDIconsData(0xE746, 'thumb_up_1'); + static const thumb_up_2_filled = _TDIconsData(0xE747, 'thumb_up_2_filled'); + static const thumb_up_2 = _TDIconsData(0xE748, 'thumb_up_2'); + static const thumb_up_filled = _TDIconsData(0xE749, 'thumb_up_filled'); + static const thumb_up = _TDIconsData(0xE74A, 'thumb_up'); + static const thunder = _TDIconsData(0xE74B, 'thunder'); + static const thunderstorm_night_filled = _TDIconsData(0xE74C, 'thunderstorm_night_filled'); + static const thunderstorm_night = _TDIconsData(0xE74D, 'thunderstorm_night'); + static const thunderstorm_sunny_filled = _TDIconsData(0xE74E, 'thunderstorm_sunny_filled'); + static const thunderstorm_sunny = _TDIconsData(0xE74F, 'thunderstorm_sunny'); + static const thunderstorm = _TDIconsData(0xE750, 'thunderstorm'); + static const ticket_filled = _TDIconsData(0xE751, 'ticket_filled'); + static const ticket = _TDIconsData(0xE752, 'ticket'); + static const time_filled = _TDIconsData(0xE753, 'time_filled'); + static const time = _TDIconsData(0xE754, 'time'); + static const tips_double_filled = _TDIconsData(0xE755, 'tips_double_filled'); + static const tips_double = _TDIconsData(0xE756, 'tips_double'); + static const tips_filled = _TDIconsData(0xE757, 'tips_filled'); + static const tips = _TDIconsData(0xE758, 'tips'); + static const tomato_filled = _TDIconsData(0xE759, 'tomato_filled'); + static const tomato = _TDIconsData(0xE75A, 'tomato'); + static const tools_circle_filled = _TDIconsData(0xE75B, 'tools_circle_filled'); + static const tools_circle = _TDIconsData(0xE75C, 'tools_circle'); + static const tools_filled = _TDIconsData(0xE75D, 'tools_filled'); + static const tools = _TDIconsData(0xE75E, 'tools'); + static const tornado = _TDIconsData(0xE75F, 'tornado'); + static const tower_1_filled = _TDIconsData(0xE760, 'tower_1_filled'); + static const tower_1 = _TDIconsData(0xE761, 'tower_1'); + static const tower_2_filled = _TDIconsData(0xE762, 'tower_2_filled'); + static const tower_2 = _TDIconsData(0xE763, 'tower_2'); + static const tower_3_filled = _TDIconsData(0xE764, 'tower_3_filled'); + static const tower_3 = _TDIconsData(0xE765, 'tower_3'); + static const tower_clock_filled = _TDIconsData(0xE766, 'tower_clock_filled'); + static const tower_clock = _TDIconsData(0xE767, 'tower_clock'); + static const tower_filled = _TDIconsData(0xE768, 'tower_filled'); + static const tower = _TDIconsData(0xE769, 'tower'); + static const town_filled = _TDIconsData(0xE76A, 'town_filled'); + static const town = _TDIconsData(0xE76B, 'town'); + static const traffic_events_filled = _TDIconsData(0xE76C, 'traffic_events_filled'); + static const traffic_events = _TDIconsData(0xE76D, 'traffic_events'); + static const traffic_filled = _TDIconsData(0xE76E, 'traffic_filled'); + static const traffic = _TDIconsData(0xE76F, 'traffic'); + static const transform_1_filled = _TDIconsData(0xE770, 'transform_1_filled'); + static const transform_1 = _TDIconsData(0xE771, 'transform_1'); + static const transform_2 = _TDIconsData(0xE772, 'transform_2'); + static const transform_3 = _TDIconsData(0xE773, 'transform_3'); + static const transform_filled = _TDIconsData(0xE774, 'transform_filled'); + static const transform = _TDIconsData(0xE775, 'transform'); + static const translate_1 = _TDIconsData(0xE776, 'translate_1'); + static const translate = _TDIconsData(0xE777, 'translate'); + static const tree_round_dot_filled = _TDIconsData(0xE778, 'tree_round_dot_filled'); + static const tree_round_dot_vertical_filled = _TDIconsData(0xE779, 'tree_round_dot_vertical_filled'); + static const tree_round_dot_vertical = _TDIconsData(0xE77A, 'tree_round_dot_vertical'); + static const tree_round_dot = _TDIconsData(0xE77B, 'tree_round_dot'); + static const tree_square_dot_filled = _TDIconsData(0xE77C, 'tree_square_dot_filled'); + static const tree_square_dot_vertical_filled = _TDIconsData(0xE77D, 'tree_square_dot_vertical_filled'); + static const tree_square_dot_vertical = _TDIconsData(0xE77E, 'tree_square_dot_vertical'); + static const tree_square_dot = _TDIconsData(0xE77F, 'tree_square_dot'); + static const trending_down = _TDIconsData(0xE780, 'trending_down'); + static const trending_up = _TDIconsData(0xE781, 'trending_up'); + static const tv_1_filled = _TDIconsData(0xE782, 'tv_1_filled'); + static const tv_1 = _TDIconsData(0xE783, 'tv_1'); + static const tv_2_filled = _TDIconsData(0xE784, 'tv_2_filled'); + static const tv_2 = _TDIconsData(0xE785, 'tv_2'); + static const tv_filled = _TDIconsData(0xE786, 'tv_filled'); + static const tv = _TDIconsData(0xE787, 'tv'); + static const typography_filled = _TDIconsData(0xE788, 'typography_filled'); + static const typography = _TDIconsData(0xE789, 'typography'); + static const uncomfortable_1_filled = _TDIconsData(0xE78A, 'uncomfortable_1_filled'); + static const uncomfortable_1 = _TDIconsData(0xE78B, 'uncomfortable_1'); + static const uncomfortable_2_filled = _TDIconsData(0xE78C, 'uncomfortable_2_filled'); + static const uncomfortable_2 = _TDIconsData(0xE78D, 'uncomfortable_2'); + static const uncomfortable_filled = _TDIconsData(0xE78E, 'uncomfortable_filled'); + static const uncomfortable = _TDIconsData(0xE78F, 'uncomfortable'); + static const undertake_delivery_filled = _TDIconsData(0xE790, 'undertake_delivery_filled'); + static const undertake_delivery = _TDIconsData(0xE791, 'undertake_delivery'); + static const undertake_environment_protection_filled = _TDIconsData(0xE792, 'undertake_environment_protection_filled'); + static const undertake_environment_protection = _TDIconsData(0xE793, 'undertake_environment_protection'); + static const undertake_filled = _TDIconsData(0xE794, 'undertake_filled'); + static const undertake_hold_up_filled = _TDIconsData(0xE795, 'undertake_hold_up_filled'); + static const undertake_hold_up = _TDIconsData(0xE796, 'undertake_hold_up'); + static const undertake_transaction_filled = _TDIconsData(0xE797, 'undertake_transaction_filled'); + static const undertake_transaction = _TDIconsData(0xE798, 'undertake_transaction'); + static const undertake = _TDIconsData(0xE799, 'undertake'); + static const unfold_less = _TDIconsData(0xE79A, 'unfold_less'); + static const unfold_more = _TDIconsData(0xE79B, 'unfold_more'); + static const unhappy_1_filled = _TDIconsData(0xE79C, 'unhappy_1_filled'); + static const unhappy_1 = _TDIconsData(0xE79D, 'unhappy_1'); + static const unhappy_filled = _TDIconsData(0xE79E, 'unhappy_filled'); + static const unhappy = _TDIconsData(0xE79F, 'unhappy'); + static const uninstall_filled = _TDIconsData(0xE7A0, 'uninstall_filled'); + static const uninstall = _TDIconsData(0xE7A1, 'uninstall'); + static const upload_1 = _TDIconsData(0xE7A2, 'upload_1'); + static const upload = _TDIconsData(0xE7A3, 'upload'); + static const upscale = _TDIconsData(0xE7A4, 'upscale'); + static const usb_filled = _TDIconsData(0xE7A5, 'usb_filled'); + static const usb = _TDIconsData(0xE7A6, 'usb'); + static const user_1_filled = _TDIconsData(0xE7A7, 'user_1_filled'); + static const user_1 = _TDIconsData(0xE7A8, 'user_1'); + static const user_add_filled = _TDIconsData(0xE7A9, 'user_add_filled'); + static const user_add = _TDIconsData(0xE7AA, 'user_add'); + static const user_arrow_down_filled = _TDIconsData(0xE7AB, 'user_arrow_down_filled'); + static const user_arrow_down = _TDIconsData(0xE7AC, 'user_arrow_down'); + static const user_arrow_left_filled = _TDIconsData(0xE7AD, 'user_arrow_left_filled'); + static const user_arrow_left = _TDIconsData(0xE7AE, 'user_arrow_left'); + static const user_arrow_right_filled = _TDIconsData(0xE7AF, 'user_arrow_right_filled'); + static const user_arrow_right = _TDIconsData(0xE7B0, 'user_arrow_right'); + static const user_arrow_up_filled = _TDIconsData(0xE7B1, 'user_arrow_up_filled'); + static const user_arrow_up = _TDIconsData(0xE7B2, 'user_arrow_up'); + static const user_avatar_filled = _TDIconsData(0xE7B3, 'user_avatar_filled'); + static const user_avatar = _TDIconsData(0xE7B4, 'user_avatar'); + static const user_blocked_filled = _TDIconsData(0xE7B5, 'user_blocked_filled'); + static const user_blocked = _TDIconsData(0xE7B6, 'user_blocked'); + static const user_business_filled = _TDIconsData(0xE7B7, 'user_business_filled'); + static const user_business = _TDIconsData(0xE7B8, 'user_business'); + static const user_checked_1_filled = _TDIconsData(0xE7B9, 'user_checked_1_filled'); + static const user_checked_1 = _TDIconsData(0xE7BA, 'user_checked_1'); + static const user_checked_filled = _TDIconsData(0xE7BB, 'user_checked_filled'); + static const user_checked = _TDIconsData(0xE7BC, 'user_checked'); + static const user_circle_filled = _TDIconsData(0xE7BD, 'user_circle_filled'); + static const user_circle = _TDIconsData(0xE7BE, 'user_circle'); + static const user_clear_filled = _TDIconsData(0xE7BF, 'user_clear_filled'); + static const user_clear = _TDIconsData(0xE7C0, 'user_clear'); + static const user_error_1_filled = _TDIconsData(0xE7C1, 'user_error_1_filled'); + static const user_error_1 = _TDIconsData(0xE7C2, 'user_error_1'); + static const user_filled = _TDIconsData(0xE7C3, 'user_filled'); + static const user_invisible_filled = _TDIconsData(0xE7C4, 'user_invisible_filled'); + static const user_invisible = _TDIconsData(0xE7C5, 'user_invisible'); + static const user_list_filled = _TDIconsData(0xE7C6, 'user_list_filled'); + static const user_list = _TDIconsData(0xE7C7, 'user_list'); + static const user_locked_filled = _TDIconsData(0xE7C8, 'user_locked_filled'); + static const user_locked = _TDIconsData(0xE7C9, 'user_locked'); + static const user_marked_filled = _TDIconsData(0xE7CA, 'user_marked_filled'); + static const user_marked = _TDIconsData(0xE7CB, 'user_marked'); + static const user_password_filled = _TDIconsData(0xE7CC, 'user_password_filled'); + static const user_password = _TDIconsData(0xE7CD, 'user_password'); + static const user_safety_filled = _TDIconsData(0xE7CE, 'user_safety_filled'); + static const user_safety = _TDIconsData(0xE7CF, 'user_safety'); + static const user_search_filled = _TDIconsData(0xE7D0, 'user_search_filled'); + static const user_search = _TDIconsData(0xE7D1, 'user_search'); + static const user_setting_filled = _TDIconsData(0xE7D2, 'user_setting_filled'); + static const user_setting = _TDIconsData(0xE7D3, 'user_setting'); + static const user_talk_1_filled = _TDIconsData(0xE7D4, 'user_talk_1_filled'); + static const user_talk_1 = _TDIconsData(0xE7D5, 'user_talk_1'); + static const user_talk_filled = _TDIconsData(0xE7D6, 'user_talk_filled'); + static const user_talk_off_1_filled = _TDIconsData(0xE7D7, 'user_talk_off_1_filled'); + static const user_talk_off_1 = _TDIconsData(0xE7D8, 'user_talk_off_1'); + static const user_talk = _TDIconsData(0xE7D9, 'user_talk'); + static const user_time_filled = _TDIconsData(0xE7DA, 'user_time_filled'); + static const user_time = _TDIconsData(0xE7DB, 'user_time'); + static const user_transmit_filled = _TDIconsData(0xE7DC, 'user_transmit_filled'); + static const user_transmit = _TDIconsData(0xE7DD, 'user_transmit'); + static const user_unknown_filled = _TDIconsData(0xE7DE, 'user_unknown_filled'); + static const user_unknown = _TDIconsData(0xE7DF, 'user_unknown'); + static const user_unlocked_filled = _TDIconsData(0xE7E0, 'user_unlocked_filled'); + static const user_unlocked = _TDIconsData(0xE7E1, 'user_unlocked'); + static const user_vip_filled = _TDIconsData(0xE7E2, 'user_vip_filled'); + static const user_vip = _TDIconsData(0xE7E3, 'user_vip'); + static const user_visible_filled = _TDIconsData(0xE7E4, 'user_visible_filled'); + static const user_visible = _TDIconsData(0xE7E5, 'user_visible'); + static const user = _TDIconsData(0xE7E6, 'user'); + static const usercase_filled = _TDIconsData(0xE7E7, 'usercase_filled'); + static const usercase_link_filled = _TDIconsData(0xE7E8, 'usercase_link_filled'); + static const usercase_link = _TDIconsData(0xE7E9, 'usercase_link'); + static const usercase = _TDIconsData(0xE7EA, 'usercase'); + static const usergroup_add_filled = _TDIconsData(0xE7EB, 'usergroup_add_filled'); + static const usergroup_add = _TDIconsData(0xE7EC, 'usergroup_add'); + static const usergroup_clear_filled = _TDIconsData(0xE7ED, 'usergroup_clear_filled'); + static const usergroup_clear = _TDIconsData(0xE7EE, 'usergroup_clear'); + static const usergroup_filled = _TDIconsData(0xE7EF, 'usergroup_filled'); + static const usergroup = _TDIconsData(0xE7F0, 'usergroup'); + static const vehicle_filled = _TDIconsData(0xE7F1, 'vehicle_filled'); + static const vehicle = _TDIconsData(0xE7F2, 'vehicle'); + static const verified_filled = _TDIconsData(0xE7F3, 'verified_filled'); + static const verified = _TDIconsData(0xE7F4, 'verified'); + static const verify_filled = _TDIconsData(0xE7F5, 'verify_filled'); + static const verify = _TDIconsData(0xE7F6, 'verify'); + static const vertical_filled = _TDIconsData(0xE7F7, 'vertical_filled'); + static const vertical = _TDIconsData(0xE7F8, 'vertical'); + static const video_camera_1_filled = _TDIconsData(0xE7F9, 'video_camera_1_filled'); + static const video_camera_1 = _TDIconsData(0xE7FA, 'video_camera_1'); + static const video_camera_2_filled = _TDIconsData(0xE7FB, 'video_camera_2_filled'); + static const video_camera_2 = _TDIconsData(0xE7FC, 'video_camera_2'); + static const video_camera_3_filled = _TDIconsData(0xE7FD, 'video_camera_3_filled'); + static const video_camera_3 = _TDIconsData(0xE7FE, 'video_camera_3'); + static const video_camera_dollar_filled = _TDIconsData(0xE7FF, 'video_camera_dollar_filled'); + static const video_camera_dollar = _TDIconsData(0xE800, 'video_camera_dollar'); + static const video_camera_filled = _TDIconsData(0xE801, 'video_camera_filled'); + static const video_camera_minus_filled = _TDIconsData(0xE802, 'video_camera_minus_filled'); + static const video_camera_minus = _TDIconsData(0xE803, 'video_camera_minus'); + static const video_camera_music_filled = _TDIconsData(0xE804, 'video_camera_music_filled'); + static const video_camera_music = _TDIconsData(0xE805, 'video_camera_music'); + static const video_camera_off_filled = _TDIconsData(0xE806, 'video_camera_off_filled'); + static const video_camera_off = _TDIconsData(0xE807, 'video_camera_off'); + static const video_camera = _TDIconsData(0xE808, 'video_camera'); + static const video_filled = _TDIconsData(0xE809, 'video_filled'); + static const video_library_filled = _TDIconsData(0xE80A, 'video_library_filled'); + static const video_library = _TDIconsData(0xE80B, 'video_library'); + static const video = _TDIconsData(0xE80C, 'video'); + static const view_agenda_filled = _TDIconsData(0xE80D, 'view_agenda_filled'); + static const view_agenda = _TDIconsData(0xE80E, 'view_agenda'); + static const view_column = _TDIconsData(0xE80F, 'view_column'); + static const view_in_ar_filled = _TDIconsData(0xE810, 'view_in_ar_filled'); + static const view_in_ar = _TDIconsData(0xE811, 'view_in_ar'); + static const view_list = _TDIconsData(0xE812, 'view_list'); + static const view_module_filled = _TDIconsData(0xE813, 'view_module_filled'); + static const view_module = _TDIconsData(0xE814, 'view_module'); + static const visual_recognition_filled = _TDIconsData(0xE815, 'visual_recognition_filled'); + static const visual_recognition = _TDIconsData(0xE816, 'visual_recognition'); + static const wallet_filled = _TDIconsData(0xE817, 'wallet_filled'); + static const wallet = _TDIconsData(0xE818, 'wallet'); + static const watch_filled = _TDIconsData(0xE819, 'watch_filled'); + static const watch = _TDIconsData(0xE81A, 'watch'); + static const watermelon_filled = _TDIconsData(0xE81B, 'watermelon_filled'); + static const watermelon = _TDIconsData(0xE81C, 'watermelon'); + static const wave_bye_filled = _TDIconsData(0xE81D, 'wave_bye_filled'); + static const wave_bye = _TDIconsData(0xE81E, 'wave_bye'); + static const wave_left_filled = _TDIconsData(0xE81F, 'wave_left_filled'); + static const wave_left = _TDIconsData(0xE820, 'wave_left'); + static const wave_right_filled = _TDIconsData(0xE821, 'wave_right_filled'); + static const wave_right = _TDIconsData(0xE822, 'wave_right'); + static const wealth_1_filled = _TDIconsData(0xE823, 'wealth_1_filled'); + static const wealth_1 = _TDIconsData(0xE824, 'wealth_1'); + static const wealth_filled = _TDIconsData(0xE825, 'wealth_filled'); + static const wealth = _TDIconsData(0xE826, 'wealth'); + static const widget_filled = _TDIconsData(0xE827, 'widget_filled'); + static const widget = _TDIconsData(0xE828, 'widget'); + static const wifi_1_filled = _TDIconsData(0xE829, 'wifi_1_filled'); + static const wifi_1 = _TDIconsData(0xE82A, 'wifi_1'); + static const wifi_off_1_filled = _TDIconsData(0xE82B, 'wifi_off_1_filled'); + static const wifi_off_1 = _TDIconsData(0xE82C, 'wifi_off_1'); + static const wifi_off = _TDIconsData(0xE82D, 'wifi_off'); + static const wifi = _TDIconsData(0xE82E, 'wifi'); + static const window_1_filled = _TDIconsData(0xE82F, 'window_1_filled'); + static const window_1 = _TDIconsData(0xE830, 'window_1'); + static const window_filled = _TDIconsData(0xE831, 'window_filled'); + static const window = _TDIconsData(0xE832, 'window'); + static const windy_rain = _TDIconsData(0xE833, 'windy_rain'); + static const windy = _TDIconsData(0xE834, 'windy'); + static const wink_filled = _TDIconsData(0xE835, 'wink_filled'); + static const wink = _TDIconsData(0xE836, 'wink'); + static const work_filled = _TDIconsData(0xE837, 'work_filled'); + static const work_history_filled = _TDIconsData(0xE838, 'work_history_filled'); + static const work_history = _TDIconsData(0xE839, 'work_history'); + static const work_off_filled = _TDIconsData(0xE83A, 'work_off_filled'); + static const work_off = _TDIconsData(0xE83B, 'work_off'); + static const work = _TDIconsData(0xE83C, 'work'); + static const wry_smile_filled = _TDIconsData(0xE83D, 'wry_smile_filled'); + static const wry_smile = _TDIconsData(0xE83E, 'wry_smile'); + static const zoom_in_filled = _TDIconsData(0xE83F, 'zoom_in_filled'); + static const zoom_in = _TDIconsData(0xE840, 'zoom_in'); + static const zoom_out_filled = _TDIconsData(0xE841, 'zoom_out_filled'); + static const zoom_out = _TDIconsData(0xE842, 'zoom_out'); + + static const all = { 'accessibility_filled': accessibility_filled, + 'accessibility': accessibility, + 'activity_filled': activity_filled, + 'activity': activity, + 'add_and_subtract': add_and_subtract, + 'add_circle_filled': add_circle_filled, + 'add_circle': add_circle, + 'add_rectangle_filled': add_rectangle_filled, + 'add_rectangle': add_rectangle, + 'add': add, + 'address_book_filled': address_book_filled, + 'address_book': address_book, + 'adjustment_filled': adjustment_filled, + 'adjustment': adjustment, + 'airplay_wave_filled': airplay_wave_filled, + 'airplay_wave': airplay_wave, + 'alarm_add_filled': alarm_add_filled, + 'alarm_add': alarm_add, + 'alarm_filled': alarm_filled, + 'alarm_off_filled': alarm_off_filled, + 'alarm_off': alarm_off, + 'alarm': alarm, + 'align_top': align_top, + 'align_vertical': align_vertical, + 'alpha': alpha, + 'analytics_filled': analytics_filled, + 'analytics': analytics, + 'anchor': anchor, + 'angry_filled': angry_filled, + 'angry': angry, + 'animation_1_filled': animation_1_filled, + 'animation_1': animation_1, + 'animation_filled': animation_filled, + 'animation': animation, + 'anticlockwise_filled': anticlockwise_filled, + 'anticlockwise': anticlockwise, + 'api': api, + 'app_filled': app_filled, + 'app': app, + 'apple_filled': apple_filled, + 'apple': apple, + 'application_filled': application_filled, + 'application': application, + 'architecture_hui_style_filled': architecture_hui_style_filled, + 'architecture_hui_style': architecture_hui_style, + 'archway_1_filled': archway_1_filled, + 'archway_1': archway_1, + 'archway_filled': archway_filled, + 'archway': archway, + 'arrow_down_circle_filled': arrow_down_circle_filled, + 'arrow_down_circle': arrow_down_circle, + 'arrow_down_rectangle_filled': arrow_down_rectangle_filled, + 'arrow_down_rectangle': arrow_down_rectangle, + 'arrow_down': arrow_down, + 'arrow_left_circle_filled': arrow_left_circle_filled, + 'arrow_left_circle': arrow_left_circle, + 'arrow_left_down_circle_filled': arrow_left_down_circle_filled, + 'arrow_left_down_circle': arrow_left_down_circle, + 'arrow_left_down': arrow_left_down, + 'arrow_left_right_1': arrow_left_right_1, + 'arrow_left_right_2': arrow_left_right_2, + 'arrow_left_right_3': arrow_left_right_3, + 'arrow_left_right_circle_filled': arrow_left_right_circle_filled, + 'arrow_left_right_circle': arrow_left_right_circle, + 'arrow_left_up_circle_filled': arrow_left_up_circle_filled, + 'arrow_left_up_circle': arrow_left_up_circle, + 'arrow_left_up': arrow_left_up, + 'arrow_left': arrow_left, + 'arrow_right_circle_filled': arrow_right_circle_filled, + 'arrow_right_circle': arrow_right_circle, + 'arrow_right_down_circle_filled': arrow_right_down_circle_filled, + 'arrow_right_down_circle': arrow_right_down_circle, + 'arrow_right_down': arrow_right_down, + 'arrow_right_up_circle_filled': arrow_right_up_circle_filled, + 'arrow_right_up_circle': arrow_right_up_circle, + 'arrow_right_up': arrow_right_up, + 'arrow_right': arrow_right, + 'arrow_triangle_down_filled': arrow_triangle_down_filled, + 'arrow_triangle_down': arrow_triangle_down, + 'arrow_triangle_up_filled': arrow_triangle_up_filled, + 'arrow_triangle_up': arrow_triangle_up, + 'arrow_up_circle_filled': arrow_up_circle_filled, + 'arrow_up_circle': arrow_up_circle, + 'arrow_up_down_1': arrow_up_down_1, + 'arrow_up_down_2': arrow_up_down_2, + 'arrow_up_down_3': arrow_up_down_3, + 'arrow_up_down_circle_filled': arrow_up_down_circle_filled, + 'arrow_up_down_circle': arrow_up_down_circle, + 'arrow_up': arrow_up, + 'artboard': artboard, + 'article_filled': article_filled, + 'article': article, + 'assignment_checked_filled': assignment_checked_filled, + 'assignment_checked': assignment_checked, + 'assignment_code_filled': assignment_code_filled, + 'assignment_code': assignment_code, + 'assignment_error_filled': assignment_error_filled, + 'assignment_error': assignment_error, + 'assignment_filled': assignment_filled, + 'assignment_user_filled': assignment_user_filled, + 'assignment_user': assignment_user, + 'assignment': assignment, + 'attach': attach, + 'attic_1_filled': attic_1_filled, + 'attic_1': attic_1, + 'attic_filled': attic_filled, + 'attic': attic, + 'audio_filled': audio_filled, + 'audio': audio, + 'awkward_filled': awkward_filled, + 'awkward': awkward, + 'backtop_rectangle_filled': backtop_rectangle_filled, + 'backtop_rectangle': backtop_rectangle, + 'backtop': backtop, + 'backup_filled': backup_filled, + 'backup': backup, + 'backward_filled': backward_filled, + 'backward': backward, + 'bad_laugh_filled': bad_laugh_filled, + 'bad_laugh': bad_laugh, + 'bamboo_shoot_filled': bamboo_shoot_filled, + 'bamboo_shoot': bamboo_shoot, + 'banana_filled': banana_filled, + 'banana': banana, + 'barbecue_filled': barbecue_filled, + 'barbecue': barbecue, + 'barcode_1': barcode_1, + 'barcode': barcode, + 'base_station': base_station, + 'battery_add_filled': battery_add_filled, + 'battery_add': battery_add, + 'battery_charging_filled': battery_charging_filled, + 'battery_charging': battery_charging, + 'battery_filled': battery_filled, + 'battery_low_filled': battery_low_filled, + 'battery_low': battery_low, + 'battery': battery, + 'bean_filled': bean_filled, + 'bean': bean, + 'beer_filled': beer_filled, + 'beer': beer, + 'beta': beta, + 'bifurcate_filled': bifurcate_filled, + 'bifurcate': bifurcate, + 'bill_filled': bill_filled, + 'bill': bill, + 'bluetooth': bluetooth, + 'bone_filled': bone_filled, + 'bone': bone, + 'book_filled': book_filled, + 'book_open_filled': book_open_filled, + 'book_open': book_open, + 'book_unknown_filled': book_unknown_filled, + 'book_unknown': book_unknown, + 'book': book, + 'bookmark_add_filled': bookmark_add_filled, + 'bookmark_add': bookmark_add, + 'bookmark_checked_filled': bookmark_checked_filled, + 'bookmark_checked': bookmark_checked, + 'bookmark_double_filled': bookmark_double_filled, + 'bookmark_double': bookmark_double, + 'bookmark_filled': bookmark_filled, + 'bookmark_minus_filled': bookmark_minus_filled, + 'bookmark_minus': bookmark_minus, + 'bookmark': bookmark, + 'braces': braces, + 'brackets': brackets, + 'bread_filled': bread_filled, + 'bread': bread, + 'bridge_1_filled': bridge_1_filled, + 'bridge_1': bridge_1, + 'bridge_2_filled': bridge_2_filled, + 'bridge_2': bridge_2, + 'bridge_3': bridge_3, + 'bridge_4': bridge_4, + 'bridge_5_filled': bridge_5_filled, + 'bridge_5': bridge_5, + 'bridge_6_filled': bridge_6_filled, + 'bridge_6': bridge_6, + 'bridge': bridge, + 'brightness_1_filled': brightness_1_filled, + 'brightness_1': brightness_1, + 'brightness_filled': brightness_filled, + 'brightness': brightness, + 'broccoli_filled': broccoli_filled, + 'broccoli': broccoli, + 'browse_filled': browse_filled, + 'browse_gallery_filled': browse_gallery_filled, + 'browse_gallery': browse_gallery, + 'browse_off_filled': browse_off_filled, + 'browse_off': browse_off, + 'browse': browse, + 'brush_filled': brush_filled, + 'brush': brush, + 'bug_filled': bug_filled, + 'bug_report_filled': bug_report_filled, + 'bug_report': bug_report, + 'bug': bug, + 'building_1_filled': building_1_filled, + 'building_1': building_1, + 'building_2_filled': building_2_filled, + 'building_2': building_2, + 'building_3_filled': building_3_filled, + 'building_3': building_3, + 'building_4_filled': building_4_filled, + 'building_4': building_4, + 'building_5_filled': building_5_filled, + 'building_5': building_5, + 'building_filled': building_filled, + 'building': building, + 'bulletpoint': bulletpoint, + 'button_filled': button_filled, + 'button': button, + 'cabbage_filled': cabbage_filled, + 'cabbage': cabbage, + 'cake_filled': cake_filled, + 'cake': cake, + 'calculation_1_filled': calculation_1_filled, + 'calculation_1': calculation_1, + 'calculation': calculation, + 'calculator_1': calculator_1, + 'calculator_filled': calculator_filled, + 'calculator': calculator, + 'calendar_1_filled': calendar_1_filled, + 'calendar_1': calendar_1, + 'calendar_2_filled': calendar_2_filled, + 'calendar_2': calendar_2, + 'calendar_edit_filled': calendar_edit_filled, + 'calendar_edit': calendar_edit, + 'calendar_event_filled': calendar_event_filled, + 'calendar_event': calendar_event, + 'calendar_filled': calendar_filled, + 'calendar': calendar, + 'call_1_filled': call_1_filled, + 'call_1': call_1, + 'call_cancel_filled': call_cancel_filled, + 'call_cancel': call_cancel, + 'call_filled': call_filled, + 'call_forwarded_filled': call_forwarded_filled, + 'call_forwarded': call_forwarded, + 'call_incoming_filled': call_incoming_filled, + 'call_incoming': call_incoming, + 'call_off_filled': call_off_filled, + 'call_off': call_off, + 'call': call, + 'calm_1_filled': calm_1_filled, + 'calm_1': calm_1, + 'calm_filled': calm_filled, + 'calm': calm, + 'camera_1_filled': camera_1_filled, + 'camera_1': camera_1, + 'camera_2_filled': camera_2_filled, + 'camera_2': camera_2, + 'camera_filled': camera_filled, + 'camera_off_filled': camera_off_filled, + 'camera_off': camera_off, + 'camera': camera, + 'candy_filled': candy_filled, + 'candy': candy, + 'card_filled': card_filled, + 'card': card, + 'cardmembership_filled': cardmembership_filled, + 'cardmembership': cardmembership, + 'caret_down_small': caret_down_small, + 'caret_down': caret_down, + 'caret_left_small': caret_left_small, + 'caret_left': caret_left, + 'caret_right_small': caret_right_small, + 'caret_right': caret_right, + 'caret_up_small': caret_up_small, + 'caret_up': caret_up, + 'cart_add_filled': cart_add_filled, + 'cart_add': cart_add, + 'cart_filled': cart_filled, + 'cart': cart, + 'cast_filled': cast_filled, + 'cast': cast, + 'castle_1_filled': castle_1_filled, + 'castle_1': castle_1, + 'castle_2_filled': castle_2_filled, + 'castle_2': castle_2, + 'castle_3_filled': castle_3_filled, + 'castle_3': castle_3, + 'castle_4_filled': castle_4_filled, + 'castle_4': castle_4, + 'castle_5_filled': castle_5_filled, + 'castle_5': castle_5, + 'castle_6_filled': castle_6_filled, + 'castle_6': castle_6, + 'castle_7_filled': castle_7_filled, + 'castle_7': castle_7, + 'castle_filled': castle_filled, + 'castle': castle, + 'cat_filled': cat_filled, + 'cat': cat, + 'catalog_filled': catalog_filled, + 'catalog': catalog, + 'cd_filled': cd_filled, + 'cd': cd, + 'celsius': celsius, + 'center_focus_strong_filled': center_focus_strong_filled, + 'center_focus_strong': center_focus_strong, + 'centimeter': centimeter, + 'certificate_1_filled': certificate_1_filled, + 'certificate_1': certificate_1, + 'certificate_filled': certificate_filled, + 'certificate': certificate, + 'chart_3d_filled': chart_3d_filled, + 'chart_3d': chart_3d, + 'chart_add_filled': chart_add_filled, + 'chart_add': chart_add, + 'chart_analytics': chart_analytics, + 'chart_area_filled': chart_area_filled, + 'chart_area_multi_filled': chart_area_multi_filled, + 'chart_area_multi': chart_area_multi, + 'chart_area': chart_area, + 'chart_bar_filled': chart_bar_filled, + 'chart_bar': chart_bar, + 'chart_bubble_filled': chart_bubble_filled, + 'chart_bubble': chart_bubble, + 'chart_column_filled': chart_column_filled, + 'chart_column': chart_column, + 'chart_combo_filled': chart_combo_filled, + 'chart_combo': chart_combo, + 'chart_filled': chart_filled, + 'chart_line_data_1': chart_line_data_1, + 'chart_line_data': chart_line_data, + 'chart_line_multi': chart_line_multi, + 'chart_line': chart_line, + 'chart_maximum': chart_maximum, + 'chart_median': chart_median, + 'chart_minimum': chart_minimum, + 'chart_pie_filled': chart_pie_filled, + 'chart_pie': chart_pie, + 'chart_radar_filled': chart_radar_filled, + 'chart_radar': chart_radar, + 'chart_radial': chart_radial, + 'chart_ring_1_filled': chart_ring_1_filled, + 'chart_ring_1': chart_ring_1, + 'chart_ring_filled': chart_ring_filled, + 'chart_ring': chart_ring, + 'chart_scatter': chart_scatter, + 'chart_stacked_filled': chart_stacked_filled, + 'chart_stacked': chart_stacked, + 'chart': chart, + 'chat_add_filled': chat_add_filled, + 'chat_add': chat_add, + 'chat_bubble_1_filled': chat_bubble_1_filled, + 'chat_bubble_1': chat_bubble_1, + 'chat_bubble_add_filled': chat_bubble_add_filled, + 'chat_bubble_add': chat_bubble_add, + 'chat_bubble_error_filled': chat_bubble_error_filled, + 'chat_bubble_error': chat_bubble_error, + 'chat_bubble_filled': chat_bubble_filled, + 'chat_bubble_help_filled': chat_bubble_help_filled, + 'chat_bubble_help': chat_bubble_help, + 'chat_bubble_history_filled': chat_bubble_history_filled, + 'chat_bubble_history': chat_bubble_history, + 'chat_bubble_locked_filled': chat_bubble_locked_filled, + 'chat_bubble_locked': chat_bubble_locked, + 'chat_bubble_smile_filled': chat_bubble_smile_filled, + 'chat_bubble_smile': chat_bubble_smile, + 'chat_bubble': chat_bubble, + 'chat_checked_filled': chat_checked_filled, + 'chat_checked': chat_checked, + 'chat_clear_filled': chat_clear_filled, + 'chat_clear': chat_clear, + 'chat_double_filled': chat_double_filled, + 'chat_double': chat_double, + 'chat_error_filled': chat_error_filled, + 'chat_error': chat_error, + 'chat_filled': chat_filled, + 'chat_heart_filled': chat_heart_filled, + 'chat_heart': chat_heart, + 'chat_message_filled': chat_message_filled, + 'chat_message': chat_message, + 'chat_off_filled': chat_off_filled, + 'chat_off': chat_off, + 'chat_poll_filled': chat_poll_filled, + 'chat_poll': chat_poll, + 'chat_setting_filled': chat_setting_filled, + 'chat_setting': chat_setting, + 'chat': chat, + 'check_circle_filled': check_circle_filled, + 'check_circle': check_circle, + 'check_double': check_double, + 'check_rectangle_filled': check_rectangle_filled, + 'check_rectangle': check_rectangle, + 'check': check, + 'cheese_filled': cheese_filled, + 'cheese': cheese, + 'cherry_filled': cherry_filled, + 'cherry': cherry, + 'chevron_down_circle_filled': chevron_down_circle_filled, + 'chevron_down_circle': chevron_down_circle, + 'chevron_down_double_s': chevron_down_double_s, + 'chevron_down_double': chevron_down_double, + 'chevron_down_rectangle_filled': chevron_down_rectangle_filled, + 'chevron_down_rectangle': chevron_down_rectangle, + 'chevron_down_s': chevron_down_s, + 'chevron_down': chevron_down, + 'chevron_left_circle_filled': chevron_left_circle_filled, + 'chevron_left_circle': chevron_left_circle, + 'chevron_left_double_s': chevron_left_double_s, + 'chevron_left_double': chevron_left_double, + 'chevron_left_rectangle_filled': chevron_left_rectangle_filled, + 'chevron_left_rectangle': chevron_left_rectangle, + 'chevron_left_s': chevron_left_s, + 'chevron_left': chevron_left, + 'chevron_right_circle_filled': chevron_right_circle_filled, + 'chevron_right_circle': chevron_right_circle, + 'chevron_right_double_s': chevron_right_double_s, + 'chevron_right_double': chevron_right_double, + 'chevron_right_rectangle_filled': chevron_right_rectangle_filled, + 'chevron_right_rectangle': chevron_right_rectangle, + 'chevron_right_s': chevron_right_s, + 'chevron_right': chevron_right, + 'chevron_up_circle_filled': chevron_up_circle_filled, + 'chevron_up_circle': chevron_up_circle, + 'chevron_up_double_s': chevron_up_double_s, + 'chevron_up_double': chevron_up_double, + 'chevron_up_rectangle_filled': chevron_up_rectangle_filled, + 'chevron_up_rectangle': chevron_up_rectangle, + 'chevron_up_s': chevron_up_s, + 'chevron_up': chevron_up, + 'chicken': chicken, + 'chili_filled': chili_filled, + 'chili': chili, + 'chimney_1_filled': chimney_1_filled, + 'chimney_1': chimney_1, + 'chimney_2_filled': chimney_2_filled, + 'chimney_2': chimney_2, + 'chimney_filled': chimney_filled, + 'chimney': chimney, + 'chinese_cabbage_filled': chinese_cabbage_filled, + 'chinese_cabbage': chinese_cabbage, + 'church_filled': church_filled, + 'church': church, + 'circle_filled': circle_filled, + 'circle': circle, + 'city_1_filled': city_1_filled, + 'city_1': city_1, + 'city_10_filled': city_10_filled, + 'city_10': city_10, + 'city_11_filled': city_11_filled, + 'city_11': city_11, + 'city_12_filled': city_12_filled, + 'city_12': city_12, + 'city_13_filled': city_13_filled, + 'city_13': city_13, + 'city_14_filled': city_14_filled, + 'city_14': city_14, + 'city_15_filled': city_15_filled, + 'city_15': city_15, + 'city_2_filled': city_2_filled, + 'city_2': city_2, + 'city_3_filled': city_3_filled, + 'city_3': city_3, + 'city_4_filled': city_4_filled, + 'city_4': city_4, + 'city_5_filled': city_5_filled, + 'city_5': city_5, + 'city_6_filled': city_6_filled, + 'city_6': city_6, + 'city_7_filled': city_7_filled, + 'city_7': city_7, + 'city_8_filled': city_8_filled, + 'city_8': city_8, + 'city_9_filled': city_9_filled, + 'city_9': city_9, + 'city_ancient_1_filled': city_ancient_1_filled, + 'city_ancient_1': city_ancient_1, + 'city_ancient_2_filled': city_ancient_2_filled, + 'city_ancient_2': city_ancient_2, + 'city_ancient_filled': city_ancient_filled, + 'city_ancient': city_ancient, + 'city_filled': city_filled, + 'city': city, + 'clear_filled': clear_filled, + 'clear_formatting_1_filled': clear_formatting_1_filled, + 'clear_formatting_1': clear_formatting_1, + 'clear_formatting_filled': clear_formatting_filled, + 'clear_formatting': clear_formatting, + 'clear': clear, + 'close_circle_filled': close_circle_filled, + 'close_circle': close_circle, + 'close_octagon_filled': close_octagon_filled, + 'close_octagon': close_octagon, + 'close_rectangle_filled': close_rectangle_filled, + 'close_rectangle': close_rectangle, + 'close': close, + 'cloud_download': cloud_download, + 'cloud_filled': cloud_filled, + 'cloud_upload': cloud_upload, + 'cloud': cloud, + 'cloudy_day_filled': cloudy_day_filled, + 'cloudy_day': cloudy_day, + 'cloudy_night_filled': cloudy_night_filled, + 'cloudy_night_rain_filled': cloudy_night_rain_filled, + 'cloudy_night_rain': cloudy_night_rain, + 'cloudy_night': cloudy_night, + 'cloudy_rain_filled': cloudy_rain_filled, + 'cloudy_rain': cloudy_rain, + 'cloudy_sunny_filled': cloudy_sunny_filled, + 'cloudy_sunny': cloudy_sunny, + 'code_1': code_1, + 'code_off': code_off, + 'code': code, + 'cola_filled': cola_filled, + 'cola': cola, + 'collage_filled': collage_filled, + 'collage': collage, + 'collection_filled': collection_filled, + 'collection': collection, + 'color_invert_filled': color_invert_filled, + 'color_invert': color_invert, + 'combination_filled': combination_filled, + 'combination': combination, + 'command': command, + 'compass_1_filled': compass_1_filled, + 'compass_1': compass_1, + 'compass_filled': compass_filled, + 'compass': compass, + 'component_breadcrumb_filled': component_breadcrumb_filled, + 'component_breadcrumb': component_breadcrumb, + 'component_checkbox_filled': component_checkbox_filled, + 'component_checkbox': component_checkbox, + 'component_divider_horizontal_filled': component_divider_horizontal_filled, + 'component_divider_horizontal': component_divider_horizontal, + 'component_divider_vertical_filled': component_divider_vertical_filled, + 'component_divider_vertical': component_divider_vertical, + 'component_dropdown_filled': component_dropdown_filled, + 'component_dropdown': component_dropdown, + 'component_grid_filled': component_grid_filled, + 'component_grid': component_grid, + 'component_input_filled': component_input_filled, + 'component_input': component_input, + 'component_layout_filled': component_layout_filled, + 'component_layout': component_layout, + 'component_radio': component_radio, + 'component_space_filled': component_space_filled, + 'component_space': component_space, + 'component_steps_filled': component_steps_filled, + 'component_steps': component_steps, + 'component_switch_filled': component_switch_filled, + 'component_switch': component_switch, + 'constraint': constraint, + 'contrast_1_filled': contrast_1_filled, + 'contrast_1': contrast_1, + 'contrast_filled': contrast_filled, + 'contrast': contrast, + 'control_platform_filled': control_platform_filled, + 'control_platform': control_platform, + 'cooperate_filled': cooperate_filled, + 'cooperate': cooperate, + 'coordinate_system_filled': coordinate_system_filled, + 'coordinate_system': coordinate_system, + 'copy_filled': copy_filled, + 'copy': copy, + 'copyright_filled': copyright_filled, + 'copyright': copyright, + 'corn_filled': corn_filled, + 'corn': corn, + 'coupon_filled': coupon_filled, + 'coupon': coupon, + 'course_filled': course_filled, + 'course': course, + 'cpu_filled': cpu_filled, + 'cpu': cpu, + 'crack_filled': crack_filled, + 'crack': crack, + 'creditcard_add_filled': creditcard_add_filled, + 'creditcard_add': creditcard_add, + 'creditcard_filled': creditcard_filled, + 'creditcard_off_filled': creditcard_off_filled, + 'creditcard_off': creditcard_off, + 'creditcard': creditcard, + 'crooked_smile_filled': crooked_smile_filled, + 'crooked_smile': crooked_smile, + 'cry_and_laugh_filled': cry_and_laugh_filled, + 'cry_and_laugh': cry_and_laugh, + 'cry_loudly_filled': cry_loudly_filled, + 'cry_loudly': cry_loudly, + 'css3_filled': css3_filled, + 'css3': css3, + 'cucumber': cucumber, + 'currency_exchange': currency_exchange, + 'cursor_filled': cursor_filled, + 'cursor': cursor, + 'curtain_filled': curtain_filled, + 'curtain': curtain, + 'curve': curve, + 'cut_1': cut_1, + 'cut': cut, + 'dam_1_filled': dam_1_filled, + 'dam_1': dam_1, + 'dam_2_filled': dam_2_filled, + 'dam_2': dam_2, + 'dam_3_filled': dam_3_filled, + 'dam_3': dam_3, + 'dam_4_filled': dam_4_filled, + 'dam_4': dam_4, + 'dam_5_filled': dam_5_filled, + 'dam_5': dam_5, + 'dam_6_filled': dam_6_filled, + 'dam_6': dam_6, + 'dam_7_filled': dam_7_filled, + 'dam_7': dam_7, + 'dam_filled': dam_filled, + 'dam': dam, + 'dart_board_filled': dart_board_filled, + 'dart_board': dart_board, + 'dashboard_1_filled': dashboard_1_filled, + 'dashboard_1': dashboard_1, + 'dashboard_filled': dashboard_filled, + 'dashboard': dashboard, + 'data_base_filled': data_base_filled, + 'data_base': data_base, + 'data_checked_filled': data_checked_filled, + 'data_checked': data_checked, + 'data_display': data_display, + 'data_error_filled': data_error_filled, + 'data_error': data_error, + 'data_filled': data_filled, + 'data_search_filled': data_search_filled, + 'data_search': data_search, + 'data': data, + 'delete_1_filled': delete_1_filled, + 'delete_1': delete_1, + 'delete_filled': delete_filled, + 'delete_time_filled': delete_time_filled, + 'delete_time': delete_time, + 'delete': delete, + 'delta_filled': delta_filled, + 'delta': delta, + 'depressed_filled': depressed_filled, + 'depressed': depressed, + 'desktop_1_filled': desktop_1_filled, + 'desktop_1': desktop_1, + 'desktop_filled': desktop_filled, + 'desktop': desktop, + 'despise_filled': despise_filled, + 'despise': despise, + 'device_filled': device_filled, + 'device': device, + 'discount_filled': discount_filled, + 'discount': discount, + 'dissatisfaction_filled': dissatisfaction_filled, + 'dissatisfaction': dissatisfaction, + 'divide': divide, + 'dividers_1': dividers_1, + 'dividers': dividers, + 'doge_filled': doge_filled, + 'doge': doge, + 'double_storey_filled': double_storey_filled, + 'double_storey': double_storey, + 'download_1': download_1, + 'download_2_filled': download_2_filled, + 'download_2': download_2, + 'download': download, + 'downscale': downscale, + 'drag_drop': drag_drop, + 'drag_move': drag_move, + 'drink_filled': drink_filled, + 'drink': drink, + 'drumstick_filled': drumstick_filled, + 'drumstick': drumstick, + 'dv_filled': dv_filled, + 'dv': dv, + 'dvd_filled': dvd_filled, + 'dvd': dvd, + 'earphone_filled': earphone_filled, + 'earphone': earphone, + 'earth_filled': earth_filled, + 'earth': earth, + 'edit_1_filled': edit_1_filled, + 'edit_1': edit_1, + 'edit_2_filled': edit_2_filled, + 'edit_2': edit_2, + 'edit_filled': edit_filled, + 'edit_off_filled': edit_off_filled, + 'edit_off': edit_off, + 'edit': edit, + 'education_filled': education_filled, + 'education': education, + 'eggplant_filled': eggplant_filled, + 'eggplant': eggplant, + 'ellipsis': ellipsis, + 'emo_emotional_filled': emo_emotional_filled, + 'emo_emotional': emo_emotional, + 'enter': enter, + 'equal': equal, + 'error_circle_filled': error_circle_filled, + 'error_circle': error_circle, + 'error_triangle_filled': error_triangle_filled, + 'error_triangle': error_triangle, + 'error': error, + 'excited_1_filled': excited_1_filled, + 'excited_1': excited_1, + 'excited_filled': excited_filled, + 'excited': excited, + 'expand_down_filled': expand_down_filled, + 'expand_down': expand_down, + 'expand_horizontal': expand_horizontal, + 'expand_up_filled': expand_up_filled, + 'expand_up': expand_up, + 'expand_vertical': expand_vertical, + 'explore_filled': explore_filled, + 'explore_off_filled': explore_off_filled, + 'explore_off': explore_off, + 'explore': explore, + 'exposure_filled': exposure_filled, + 'exposure': exposure, + 'extension_filled': extension_filled, + 'extension_off_filled': extension_off_filled, + 'extension_off': extension_off, + 'extension': extension, + 'face_retouching_filled': face_retouching_filled, + 'face_retouching': face_retouching, + 'fact_check_filled': fact_check_filled, + 'fact_check': fact_check, + 'fahrenheit_scale': fahrenheit_scale, + 'feel_at_ease_filled': feel_at_ease_filled, + 'feel_at_ease': feel_at_ease, + 'ferocious_filled': ferocious_filled, + 'ferocious': ferocious, + 'ferris_wheel_filled': ferris_wheel_filled, + 'ferris_wheel': ferris_wheel, + 'file_1_filled': file_1_filled, + 'file_1': file_1, + 'file_add_1_filled': file_add_1_filled, + 'file_add_1': file_add_1, + 'file_add_filled': file_add_filled, + 'file_add': file_add, + 'file_attachment_filled': file_attachment_filled, + 'file_attachment': file_attachment, + 'file_blocked_filled': file_blocked_filled, + 'file_blocked': file_blocked, + 'file_code_1_filled': file_code_1_filled, + 'file_code_1': file_code_1, + 'file_code_filled': file_code_filled, + 'file_code': file_code, + 'file_copy_filled': file_copy_filled, + 'file_copy': file_copy, + 'file_download_filled': file_download_filled, + 'file_download': file_download, + 'file_excel_filled': file_excel_filled, + 'file_excel': file_excel, + 'file_export_filled': file_export_filled, + 'file_export': file_export, + 'file_filled': file_filled, + 'file_icon_filled': file_icon_filled, + 'file_icon': file_icon, + 'file_image_filled': file_image_filled, + 'file_image': file_image, + 'file_import_filled': file_import_filled, + 'file_import': file_import, + 'file_locked_filled': file_locked_filled, + 'file_locked': file_locked, + 'file_minus_filled': file_minus_filled, + 'file_minus': file_minus, + 'file_music_filled': file_music_filled, + 'file_music': file_music, + 'file_onenote_filled': file_onenote_filled, + 'file_onenote': file_onenote, + 'file_outlook_filled': file_outlook_filled, + 'file_outlook': file_outlook, + 'file_paste_filled': file_paste_filled, + 'file_paste': file_paste, + 'file_pdf_filled': file_pdf_filled, + 'file_pdf': file_pdf, + 'file_powerpoint_filled': file_powerpoint_filled, + 'file_powerpoint': file_powerpoint, + 'file_restore_filled': file_restore_filled, + 'file_restore': file_restore, + 'file_safety_filled': file_safety_filled, + 'file_safety': file_safety, + 'file_search_filled': file_search_filled, + 'file_search': file_search, + 'file_setting_filled': file_setting_filled, + 'file_setting': file_setting, + 'file_teams_filled': file_teams_filled, + 'file_teams': file_teams, + 'file_transmit_double_filled': file_transmit_double_filled, + 'file_transmit_double': file_transmit_double, + 'file_transmit_filled': file_transmit_filled, + 'file_transmit': file_transmit, + 'file_unknown_filled': file_unknown_filled, + 'file_unknown': file_unknown, + 'file_unlocked_filled': file_unlocked_filled, + 'file_unlocked': file_unlocked, + 'file_word_filled': file_word_filled, + 'file_word': file_word, + 'file_zip_filled': file_zip_filled, + 'file_zip': file_zip, + 'file': file, + 'fill_color_1_filled': fill_color_1_filled, + 'fill_color_1': fill_color_1, + 'fill_color_filled': fill_color_filled, + 'fill_color': fill_color, + 'film_1_filled': film_1_filled, + 'film_1': film_1, + 'film_filled': film_filled, + 'film': film, + 'filter_1_filled': filter_1_filled, + 'filter_1': filter_1, + 'filter_2_filled': filter_2_filled, + 'filter_2': filter_2, + 'filter_3_filled': filter_3_filled, + 'filter_3': filter_3, + 'filter_clear_filled': filter_clear_filled, + 'filter_clear': filter_clear, + 'filter_filled': filter_filled, + 'filter_off_filled': filter_off_filled, + 'filter_off': filter_off, + 'filter_sort_filled': filter_sort_filled, + 'filter_sort': filter_sort, + 'filter': filter, + 'fingerprint_1': fingerprint_1, + 'fingerprint_2': fingerprint_2, + 'fingerprint_3': fingerprint_3, + 'fingerprint': fingerprint, + 'fish_filled': fish_filled, + 'fish': fish, + 'flag_1_filled': flag_1_filled, + 'flag_1': flag_1, + 'flag_2_filled': flag_2_filled, + 'flag_2': flag_2, + 'flag_3_filled': flag_3_filled, + 'flag_3': flag_3, + 'flag_4_filled': flag_4_filled, + 'flag_4': flag_4, + 'flag_filled': flag_filled, + 'flag': flag, + 'flashlight_filled': flashlight_filled, + 'flashlight': flashlight, + 'flight_landing_filled': flight_landing_filled, + 'flight_landing': flight_landing, + 'flight_takeoff_filled': flight_takeoff_filled, + 'flight_takeoff': flight_takeoff, + 'flip_smiling_face_filled': flip_smiling_face_filled, + 'flip_smiling_face': flip_smiling_face, + 'flip_to_back_filled': flip_to_back_filled, + 'flip_to_back': flip_to_back, + 'flip_to_front_filled': flip_to_front_filled, + 'flip_to_front': flip_to_front, + 'focus_filled': focus_filled, + 'focus': focus, + 'fog_filled': fog_filled, + 'fog_night_filled': fog_night_filled, + 'fog_night': fog_night, + 'fog_sunny_filled': fog_sunny_filled, + 'fog_sunny': fog_sunny, + 'fog': fog, + 'folder_1_filled': folder_1_filled, + 'folder_1': folder_1, + 'folder_add_1_filled': folder_add_1_filled, + 'folder_add_1': folder_add_1, + 'folder_add_filled': folder_add_filled, + 'folder_add': folder_add, + 'folder_blocked_filled': folder_blocked_filled, + 'folder_blocked': folder_blocked, + 'folder_details_filled': folder_details_filled, + 'folder_details': folder_details, + 'folder_export_filled': folder_export_filled, + 'folder_export': folder_export, + 'folder_filled': folder_filled, + 'folder_import_filled': folder_import_filled, + 'folder_import': folder_import, + 'folder_locked_filled': folder_locked_filled, + 'folder_locked': folder_locked, + 'folder_minus_filled': folder_minus_filled, + 'folder_minus': folder_minus, + 'folder_move_filled': folder_move_filled, + 'folder_move': folder_move, + 'folder_off_filled': folder_off_filled, + 'folder_off': folder_off, + 'folder_open_1_filled': folder_open_1_filled, + 'folder_open_1': folder_open_1, + 'folder_open_filled': folder_open_filled, + 'folder_open': folder_open, + 'folder_search_filled': folder_search_filled, + 'folder_search': folder_search, + 'folder_setting_filled': folder_setting_filled, + 'folder_setting': folder_setting, + 'folder_shared_filled': folder_shared_filled, + 'folder_shared': folder_shared, + 'folder_unlocked_filled': folder_unlocked_filled, + 'folder_unlocked': folder_unlocked, + 'folder_zip_filled': folder_zip_filled, + 'folder_zip': folder_zip, + 'folder': folder, + 'forest_filled': forest_filled, + 'forest': forest, + 'fork_filled': fork_filled, + 'fork': fork, + 'form_filled': form_filled, + 'form': form, + 'format_horizontal_align_bottom': format_horizontal_align_bottom, + 'format_horizontal_align_center': format_horizontal_align_center, + 'format_horizontal_align_top': format_horizontal_align_top, + 'format_vertical_align_center': format_vertical_align_center, + 'format_vertical_align_left': format_vertical_align_left, + 'format_vertical_align_right': format_vertical_align_right, + 'forward_filled': forward_filled, + 'forward': forward, + 'frame_1_filled': frame_1_filled, + 'frame_1': frame_1, + 'frame_filled': frame_filled, + 'frame': frame, + 'fries_filled': fries_filled, + 'fries': fries, + 'fullscreen_1': fullscreen_1, + 'fullscreen_2': fullscreen_2, + 'fullscreen_exit_1': fullscreen_exit_1, + 'fullscreen_exit': fullscreen_exit, + 'fullscreen': fullscreen, + 'function_curve': function_curve, + 'functions_1': functions_1, + 'functions': functions, + 'gamepad_1_filled': gamepad_1_filled, + 'gamepad_1': gamepad_1, + 'gamepad_filled': gamepad_filled, + 'gamepad': gamepad, + 'gamma': gamma, + 'garlic_filled': garlic_filled, + 'garlic': garlic, + 'gender_female': gender_female, + 'gender_male': gender_male, + 'gesture_applause_filled': gesture_applause_filled, + 'gesture_applause': gesture_applause, + 'gesture_click_filled': gesture_click_filled, + 'gesture_click': gesture_click, + 'gesture_down_filled': gesture_down_filled, + 'gesture_down': gesture_down, + 'gesture_expansion_filled': gesture_expansion_filled, + 'gesture_expansion': gesture_expansion, + 'gesture_left_filled': gesture_left_filled, + 'gesture_left_slip_filled': gesture_left_slip_filled, + 'gesture_left_slip': gesture_left_slip, + 'gesture_left': gesture_left, + 'gesture_open_filled': gesture_open_filled, + 'gesture_open': gesture_open, + 'gesture_pray_filled': gesture_pray_filled, + 'gesture_pray': gesture_pray, + 'gesture_press_filled': gesture_press_filled, + 'gesture_press': gesture_press, + 'gesture_ranslation_filled': gesture_ranslation_filled, + 'gesture_ranslation': gesture_ranslation, + 'gesture_right_filled': gesture_right_filled, + 'gesture_right_slip_filled': gesture_right_slip_filled, + 'gesture_right_slip': gesture_right_slip, + 'gesture_right': gesture_right, + 'gesture_slide_left_and_right_filled': gesture_slide_left_and_right_filled, + 'gesture_slide_left_and_right': gesture_slide_left_and_right, + 'gesture_slide_up_filled': gesture_slide_up_filled, + 'gesture_slide_up': gesture_slide_up, + 'gesture_typing_filled': gesture_typing_filled, + 'gesture_typing': gesture_typing, + 'gesture_up_and_down_filled': gesture_up_and_down_filled, + 'gesture_up_and_down': gesture_up_and_down, + 'gesture_up_filled': gesture_up_filled, + 'gesture_up': gesture_up, + 'gesture_wipe_down_filled': gesture_wipe_down_filled, + 'gesture_wipe_down': gesture_wipe_down, + 'gift_filled': gift_filled, + 'gift': gift, + 'giggle_filled': giggle_filled, + 'giggle': giggle, + 'git_branch_filled': git_branch_filled, + 'git_branch': git_branch, + 'git_commit_filled': git_commit_filled, + 'git_commit': git_commit, + 'git_merge_filled': git_merge_filled, + 'git_merge': git_merge, + 'git_pull_request_filled': git_pull_request_filled, + 'git_pull_request': git_pull_request, + 'git_repository_commits_filled': git_repository_commits_filled, + 'git_repository_commits': git_repository_commits, + 'git_repository_filled': git_repository_filled, + 'git_repository_private_filled': git_repository_private_filled, + 'git_repository_private': git_repository_private, + 'git_repository': git_repository, + 'gps_filled': gps_filled, + 'gps': gps, + 'grape_filled': grape_filled, + 'grape': grape, + 'greater_than_or_equal': greater_than_or_equal, + 'greater_than': greater_than, + 'green_onion': green_onion, + 'grid_add_filled': grid_add_filled, + 'grid_add': grid_add, + 'grid_view_filled': grid_view_filled, + 'grid_view': grid_view, + 'guitar_filled': guitar_filled, + 'guitar': guitar, + 'hamburger_filled': hamburger_filled, + 'hamburger': hamburger, + 'happy_filled': happy_filled, + 'happy': happy, + 'hard_disk_storage_filled': hard_disk_storage_filled, + 'hard_disk_storage': hard_disk_storage, + 'hard_drive_filled': hard_drive_filled, + 'hard_drive': hard_drive, + 'hashtag': hashtag, + 'hd_filled': hd_filled, + 'hd': hd, + 'heart_filled': heart_filled, + 'heart': heart, + 'help_circle_filled': help_circle_filled, + 'help_circle': help_circle, + 'help_rectangle_filled': help_rectangle_filled, + 'help_rectangle': help_rectangle, + 'help': help, + 'highlight_1_filled': highlight_1_filled, + 'highlight_1': highlight_1, + 'highlight': highlight, + 'history_setting': history_setting, + 'history': history, + 'home_filled': home_filled, + 'home': home, + 'horizontal_filled': horizontal_filled, + 'horizontal': horizontal, + 'hospital_1_filled': hospital_1_filled, + 'hospital_1': hospital_1, + 'hospital_filled': hospital_filled, + 'hospital': hospital, + 'hotspot_wave_filled': hotspot_wave_filled, + 'hotspot_wave': hotspot_wave, + 'hourglass_filled': hourglass_filled, + 'hourglass': hourglass, + 'houses_1_filled': houses_1_filled, + 'houses_1': houses_1, + 'houses_2_filled': houses_2_filled, + 'houses_2': houses_2, + 'houses_filled': houses_filled, + 'houses': houses, + 'html5_filled': html5_filled, + 'html5': html5, + 'https_filled': https_filled, + 'https': https, + 'ice_cream_filled': ice_cream_filled, + 'ice_cream': ice_cream, + 'icon_filled': icon_filled, + 'icon': icon, + 'image_1_filled': image_1_filled, + 'image_1': image_1, + 'image_add_filled': image_add_filled, + 'image_add': image_add, + 'image_edit_filled': image_edit_filled, + 'image_edit': image_edit, + 'image_error_filled': image_error_filled, + 'image_error': image_error, + 'image_filled': image_filled, + 'image_off_filled': image_off_filled, + 'image_off': image_off, + 'image_search_filled': image_search_filled, + 'image_search': image_search, + 'image': image, + 'indent_left': indent_left, + 'indent_right': indent_right, + 'indicator_filled': indicator_filled, + 'indicator': indicator, + 'info_circle_filled': info_circle_filled, + 'info_circle': info_circle, + 'ink_filled': ink_filled, + 'ink': ink, + 'install_desktop_filled': install_desktop_filled, + 'install_desktop': install_desktop, + 'install_filled': install_filled, + 'install_mobile_filled': install_mobile_filled, + 'install_mobile': install_mobile, + 'install': install, + 'institution_checked_filled': institution_checked_filled, + 'institution_checked': institution_checked, + 'institution_filled': institution_filled, + 'institution': institution, + 'internet_filled': internet_filled, + 'internet': internet, + 'ipod_filled': ipod_filled, + 'ipod': ipod, + 'joyful_filled': joyful_filled, + 'joyful': joyful, + 'jump_double': jump_double, + 'jump_off': jump_off, + 'jump': jump, + 'key_filled': key_filled, + 'key': key, + 'keyboard_filled': keyboard_filled, + 'keyboard': keyboard, + 'laptop_filled': laptop_filled, + 'laptop': laptop, + 'layers_filled': layers_filled, + 'layers': layers, + 'layout_filled': layout_filled, + 'layout': layout, + 'leaderboard_filled': leaderboard_filled, + 'leaderboard': leaderboard, + 'lemon_filled': lemon_filled, + 'lemon_slice_filled': lemon_slice_filled, + 'lemon_slice': lemon_slice, + 'lemon': lemon, + 'less_than_or_equal': less_than_or_equal, + 'less_than': less_than, + 'letters_a': letters_a, + 'letters_b': letters_b, + 'letters_c': letters_c, + 'letters_d': letters_d, + 'letters_e': letters_e, + 'letters_f': letters_f, + 'letters_g': letters_g, + 'letters_h': letters_h, + 'letters_i': letters_i, + 'letters_j': letters_j, + 'letters_k': letters_k, + 'letters_l': letters_l, + 'letters_m': letters_m, + 'letters_n': letters_n, + 'letters_o': letters_o, + 'letters_p': letters_p, + 'letters_q': letters_q, + 'letters_r': letters_r, + 'letters_s': letters_s, + 'letters_t': letters_t, + 'letters_u': letters_u, + 'letters_v': letters_v, + 'letters_w': letters_w, + 'letters_x': letters_x, + 'letters_y': letters_y, + 'letters_z': letters_z, + 'lightbulb_circle_filled': lightbulb_circle_filled, + 'lightbulb_circle': lightbulb_circle, + 'lightbulb_filled': lightbulb_filled, + 'lightbulb': lightbulb, + 'lighthouse_1_filled': lighthouse_1_filled, + 'lighthouse_1': lighthouse_1, + 'lighthouse_2_filled': lighthouse_2_filled, + 'lighthouse_2': lighthouse_2, + 'lighthouse_filled': lighthouse_filled, + 'lighthouse': lighthouse, + 'lighting_circle_filled': lighting_circle_filled, + 'lighting_circle': lighting_circle, + 'line_height': line_height, + 'link_1': link_1, + 'link_unlink': link_unlink, + 'link': link, + 'liquor_filled': liquor_filled, + 'liquor': liquor, + 'list_numbered': list_numbered, + 'list': list, + 'load': load, + 'loading': loading, + 'location_1_filled': location_1_filled, + 'location_1': location_1, + 'location_enlargement_filled': location_enlargement_filled, + 'location_enlargement': location_enlargement, + 'location_error_filled': location_error_filled, + 'location_error': location_error, + 'location_filled': location_filled, + 'location_parking_place_filled': location_parking_place_filled, + 'location_parking_place': location_parking_place, + 'location_reduction_filled': location_reduction_filled, + 'location_reduction': location_reduction, + 'location_setting_filled': location_setting_filled, + 'location_setting': location_setting, + 'location': location, + 'lock_off_filled': lock_off_filled, + 'lock_off': lock_off, + 'lock_on_filled': lock_on_filled, + 'lock_on': lock_on, + 'lock_time_filled': lock_time_filled, + 'lock_time': lock_time, + 'login': login, + 'logo_adobe_illustrate_filled': logo_adobe_illustrate_filled, + 'logo_adobe_illustrate': logo_adobe_illustrate, + 'logo_adobe_lightroom_filled': logo_adobe_lightroom_filled, + 'logo_adobe_lightroom': logo_adobe_lightroom, + 'logo_adobe_photoshop_filled': logo_adobe_photoshop_filled, + 'logo_adobe_photoshop': logo_adobe_photoshop, + 'logo_android_filled': logo_android_filled, + 'logo_android': logo_android, + 'logo_apple_filled': logo_apple_filled, + 'logo_apple': logo_apple, + 'logo_behance': logo_behance, + 'logo_chrome_filled': logo_chrome_filled, + 'logo_chrome': logo_chrome, + 'logo_cinema4d_filled': logo_cinema4d_filled, + 'logo_cinema4d': logo_cinema4d, + 'logo_codepen': logo_codepen, + 'logo_codesandbox': logo_codesandbox, + 'logo_dribbble_filled': logo_dribbble_filled, + 'logo_dribbble': logo_dribbble, + 'logo_facebook_filled': logo_facebook_filled, + 'logo_facebook': logo_facebook, + 'logo_figma_filled': logo_figma_filled, + 'logo_figma': logo_figma, + 'logo_framer_filled': logo_framer_filled, + 'logo_framer': logo_framer, + 'logo_github_filled': logo_github_filled, + 'logo_github': logo_github, + 'logo_gitlab_filled': logo_gitlab_filled, + 'logo_gitlab': logo_gitlab, + 'logo_ie_filled': logo_ie_filled, + 'logo_ie': logo_ie, + 'logo_instagram_filled': logo_instagram_filled, + 'logo_instagram': logo_instagram, + 'logo_qq_filled': logo_qq_filled, + 'logo_qq': logo_qq, + 'logo_twitter_filled': logo_twitter_filled, + 'logo_twitter': logo_twitter, + 'logo_wechat_stroke_filled': logo_wechat_stroke_filled, + 'logo_wechat_stroke': logo_wechat_stroke, + 'logo_wechatpay_filled': logo_wechatpay_filled, + 'logo_wechatpay': logo_wechatpay, + 'logo_wecom_filled': logo_wecom_filled, + 'logo_wecom': logo_wecom, + 'logo_windows_filled': logo_windows_filled, + 'logo_windows': logo_windows, + 'logo_youtube_filled': logo_youtube_filled, + 'logo_youtube': logo_youtube, + 'logout': logout, + 'look_around_filled': look_around_filled, + 'look_around': look_around, + 'loudspeaker_filled': loudspeaker_filled, + 'loudspeaker': loudspeaker, + 'mail_filled': mail_filled, + 'mail': mail, + 'map_3d_filled': map_3d_filled, + 'map_3d': map_3d, + 'map_add_filled': map_add_filled, + 'map_add': map_add, + 'map_aiming_filled': map_aiming_filled, + 'map_aiming': map_aiming, + 'map_blocked_filled': map_blocked_filled, + 'map_blocked': map_blocked, + 'map_bubble_filled': map_bubble_filled, + 'map_bubble': map_bubble, + 'map_cancel_filled': map_cancel_filled, + 'map_cancel': map_cancel, + 'map_chat_filled': map_chat_filled, + 'map_chat': map_chat, + 'map_checked_filled': map_checked_filled, + 'map_checked': map_checked, + 'map_collection_filled': map_collection_filled, + 'map_collection': map_collection, + 'map_connection_filled': map_connection_filled, + 'map_connection': map_connection, + 'map_distance_filled': map_distance_filled, + 'map_distance': map_distance, + 'map_double_filled': map_double_filled, + 'map_double': map_double, + 'map_edit_filled': map_edit_filled, + 'map_edit': map_edit, + 'map_filled': map_filled, + 'map_grid_filled': map_grid_filled, + 'map_grid': map_grid, + 'map_information_1_filled': map_information_1_filled, + 'map_information_1': map_information_1, + 'map_information_2_filled': map_information_2_filled, + 'map_information_2': map_information_2, + 'map_information_filled': map_information_filled, + 'map_information': map_information, + 'map_location_filled': map_location_filled, + 'map_location': map_location, + 'map_locked_filled': map_locked_filled, + 'map_locked': map_locked, + 'map_marked_filled': map_marked_filled, + 'map_marked': map_marked, + 'map_navigation_filled': map_navigation_filled, + 'map_navigation': map_navigation, + 'map_outline_filled': map_outline_filled, + 'map_outline': map_outline, + 'map_route_planning_filled': map_route_planning_filled, + 'map_route_planning': map_route_planning, + 'map_ruler_filled': map_ruler_filled, + 'map_ruler': map_ruler, + 'map_safety_filled': map_safety_filled, + 'map_safety': map_safety, + 'map_search_1_filled': map_search_1_filled, + 'map_search_1': map_search_1, + 'map_search_filled': map_search_filled, + 'map_search': map_search, + 'map_setting_filled': map_setting_filled, + 'map_setting': map_setting, + 'map_unlocked_filled': map_unlocked_filled, + 'map_unlocked': map_unlocked, + 'map': map, + 'mark_as_unread_filled': mark_as_unread_filled, + 'mark_as_unread': mark_as_unread, + 'markup_filled': markup_filled, + 'markup': markup, + 'mathematics_filled': mathematics_filled, + 'mathematics': mathematics, + 'measurement_1_filled': measurement_1_filled, + 'measurement_1': measurement_1, + 'measurement_2_filled': measurement_2_filled, + 'measurement_2': measurement_2, + 'measurement_filled': measurement_filled, + 'measurement': measurement, + 'meat_pepper_filled': meat_pepper_filled, + 'meat_pepper': meat_pepper, + 'media_library_filled': media_library_filled, + 'media_library': media_library, + 'member_filled': member_filled, + 'member': member, + 'menu_application': menu_application, + 'menu_filled': menu_filled, + 'menu_fold': menu_fold, + 'menu_unfold': menu_unfold, + 'menu': menu, + 'merge_cells_filled': merge_cells_filled, + 'merge_cells': merge_cells, + 'microphone_1_filled': microphone_1_filled, + 'microphone_1': microphone_1, + 'microphone_2_filled': microphone_2_filled, + 'microphone_2': microphone_2, + 'microphone_filled': microphone_filled, + 'microphone': microphone, + 'milk_filled': milk_filled, + 'milk': milk, + 'minus_circle_filled': minus_circle_filled, + 'minus_circle': minus_circle, + 'minus_rectangle_filled': minus_rectangle_filled, + 'minus_rectangle': minus_rectangle, + 'minus': minus, + 'mirror_filled': mirror_filled, + 'mirror': mirror, + 'mobile_blocked_filled': mobile_blocked_filled, + 'mobile_blocked': mobile_blocked, + 'mobile_filled': mobile_filled, + 'mobile_list_filled': mobile_list_filled, + 'mobile_list': mobile_list, + 'mobile_navigation_filled': mobile_navigation_filled, + 'mobile_navigation': mobile_navigation, + 'mobile_shortcut_filled': mobile_shortcut_filled, + 'mobile_shortcut': mobile_shortcut, + 'mobile_vibrate_filled': mobile_vibrate_filled, + 'mobile_vibrate': mobile_vibrate, + 'mobile': mobile, + 'mode_dark_filled': mode_dark_filled, + 'mode_dark': mode_dark, + 'mode_light_filled': mode_light_filled, + 'mode_light': mode_light, + 'module_filled': module_filled, + 'module': module, + 'money_filled': money_filled, + 'money': money, + 'monument_filled': monument_filled, + 'monument': monument, + 'moon_fall_filled': moon_fall_filled, + 'moon_fall': moon_fall, + 'moon_filled': moon_filled, + 'moon_rising_filled': moon_rising_filled, + 'moon_rising': moon_rising, + 'moon': moon, + 'more': more, + 'mosque_1_filled': mosque_1_filled, + 'mosque_1': mosque_1, + 'mosque_filled': mosque_filled, + 'mosque': mosque, + 'mouse_filled': mouse_filled, + 'mouse': mouse, + 'move_1': move_1, + 'move': move, + 'movie_clapper_filled': movie_clapper_filled, + 'movie_clapper': movie_clapper, + 'multiply': multiply, + 'museum_1_filled': museum_1_filled, + 'museum_1': museum_1, + 'museum_2_filled': museum_2_filled, + 'museum_2': museum_2, + 'museum_filled': museum_filled, + 'museum': museum, + 'mushroom_1_filled': mushroom_1_filled, + 'mushroom_1': mushroom_1, + 'mushroom_filled': mushroom_filled, + 'mushroom': mushroom, + 'music_1_filled': music_1_filled, + 'music_1': music_1, + 'music_2_filled': music_2_filled, + 'music_2': music_2, + 'music_filled': music_filled, + 'music_rectangle_add_filled': music_rectangle_add_filled, + 'music_rectangle_add': music_rectangle_add, + 'music': music, + 'navigation_arrow_filled': navigation_arrow_filled, + 'navigation_arrow': navigation_arrow, + 'next_filled': next_filled, + 'next': next, + 'no_expression_filled': no_expression_filled, + 'no_expression': no_expression, + 'noodle_filled': noodle_filled, + 'noodle': noodle, + 'notification_add_filled': notification_add_filled, + 'notification_add': notification_add, + 'notification_circle_filled': notification_circle_filled, + 'notification_circle': notification_circle, + 'notification_error_filled': notification_error_filled, + 'notification_error': notification_error, + 'notification_filled': notification_filled, + 'notification': notification, + 'numbers_0_1': numbers_0_1, + 'numbers_0': numbers_0, + 'numbers_1_1': numbers_1_1, + 'numbers_1': numbers_1, + 'numbers_2_1': numbers_2_1, + 'numbers_2': numbers_2, + 'numbers_3_1': numbers_3_1, + 'numbers_3': numbers_3, + 'numbers_4_1': numbers_4_1, + 'numbers_4': numbers_4, + 'numbers_5_1': numbers_5_1, + 'numbers_5': numbers_5, + 'numbers_6_1': numbers_6_1, + 'numbers_6': numbers_6, + 'numbers_7_1': numbers_7_1, + 'numbers_7': numbers_7, + 'numbers_8_1': numbers_8_1, + 'numbers_8': numbers_8, + 'numbers_9_1': numbers_9_1, + 'numbers_9': numbers_9, + 'nut_filled': nut_filled, + 'nut': nut, + 'object_storage': object_storage, + 'open_mouth_filled': open_mouth_filled, + 'open_mouth': open_mouth, + 'opera_filled': opera_filled, + 'opera': opera, + 'order_adjustment_column': order_adjustment_column, + 'order_ascending': order_ascending, + 'order_descending': order_descending, + 'outbox_filled': outbox_filled, + 'outbox': outbox, + 'page_first': page_first, + 'page_head_filled': page_head_filled, + 'page_head': page_head, + 'page_last': page_last, + 'palace_1_filled': palace_1_filled, + 'palace_1': palace_1, + 'palace_2_filled': palace_2_filled, + 'palace_2': palace_2, + 'palace_3_filled': palace_3_filled, + 'palace_3': palace_3, + 'palace_4_filled': palace_4_filled, + 'palace_4': palace_4, + 'palace_filled': palace_filled, + 'palace': palace, + 'palette_1_filled': palette_1_filled, + 'palette_1': palette_1, + 'palette_filled': palette_filled, + 'palette': palette, + 'panorama_horizontal_filled': panorama_horizontal_filled, + 'panorama_horizontal': panorama_horizontal, + 'panorama_vertical_filled': panorama_vertical_filled, + 'panorama_vertical': panorama_vertical, + 'pantone_filled': pantone_filled, + 'pantone': pantone, + 'parabola': parabola, + 'parentheses': parentheses, + 'paste_filled': paste_filled, + 'paste': paste, + 'patio_filled': patio_filled, + 'patio': patio, + 'pause_circle_filled': pause_circle_filled, + 'pause_circle_stroke_filled': pause_circle_stroke_filled, + 'pause_circle_stroke': pause_circle_stroke, + 'pause_circle': pause_circle, + 'pause': pause, + 'pea_filled': pea_filled, + 'pea': pea, + 'peach_filled': peach_filled, + 'peach': peach, + 'pear_filled': pear_filled, + 'pear': pear, + 'pearl_of_the_orient_filled': pearl_of_the_orient_filled, + 'pearl_of_the_orient': pearl_of_the_orient, + 'pen_ball_filled': pen_ball_filled, + 'pen_ball': pen_ball, + 'pen_brush_filled': pen_brush_filled, + 'pen_brush': pen_brush, + 'pen_filled': pen_filled, + 'pen_mark_filled': pen_mark_filled, + 'pen_mark': pen_mark, + 'pen_quill_filled': pen_quill_filled, + 'pen_quill': pen_quill, + 'pen': pen, + 'pending_filled': pending_filled, + 'pending': pending, + 'percent': percent, + 'personal_information_filled': personal_information_filled, + 'personal_information': personal_information, + 'phone_locked_filled': phone_locked_filled, + 'phone_locked': phone_locked, + 'phone_search_filled': phone_search_filled, + 'phone_search': phone_search, + 'pi': pi, + 'piano_filled': piano_filled, + 'piano': piano, + 'pin_filled': pin_filled, + 'pin': pin, + 'play_circle_filled': play_circle_filled, + 'play_circle_stroke_add_filled': play_circle_stroke_add_filled, + 'play_circle_stroke_add': play_circle_stroke_add, + 'play_circle_stroke_filled': play_circle_stroke_filled, + 'play_circle_stroke': play_circle_stroke, + 'play_circle': play_circle, + 'play_demo_filled': play_demo_filled, + 'play_demo': play_demo, + 'play_rectangle_filled': play_rectangle_filled, + 'play_rectangle': play_rectangle, + 'play': play, + 'plus': plus, + 'popsicle_filled': popsicle_filled, + 'popsicle': popsicle, + 'portrait_filled': portrait_filled, + 'portrait': portrait, + 'pout_filled': pout_filled, + 'pout': pout, + 'poweroff': poweroff, + 'precise_monitor': precise_monitor, + 'previous_filled': previous_filled, + 'previous': previous, + 'print_filled': print_filled, + 'print': print, + 'pumpkin_filled': pumpkin_filled, + 'pumpkin': pumpkin, + 'pyramid_filled': pyramid_filled, + 'pyramid_maya_filled': pyramid_maya_filled, + 'pyramid_maya': pyramid_maya, + 'pyramid': pyramid, + 'qrcode': qrcode, + 'quadratic': quadratic, + 'questionnaire_double_filled': questionnaire_double_filled, + 'questionnaire_double': questionnaire_double, + 'questionnaire_filled': questionnaire_filled, + 'questionnaire': questionnaire, + 'queue_filled': queue_filled, + 'queue': queue, + 'radar': radar, + 'radio_1_filled': radio_1_filled, + 'radio_1': radio_1, + 'radio_2_filled': radio_2_filled, + 'radio_2': radio_2, + 'radish_filled': radish_filled, + 'radish': radish, + 'rain_heavy': rain_heavy, + 'rain_light_filled': rain_light_filled, + 'rain_light': rain_light, + 'rain_medium': rain_medium, + 'rainbow': rainbow, + 'rectangle_filled': rectangle_filled, + 'rectangle': rectangle, + 'refresh': refresh, + 'relation': relation, + 'relativity_filled': relativity_filled, + 'relativity': relativity, + 'remote_wave_filled': remote_wave_filled, + 'remote_wave': remote_wave, + 'remove': remove, + 'replay_filled': replay_filled, + 'replay': replay, + 'rice_ball_filled': rice_ball_filled, + 'rice_ball': rice_ball, + 'rice_filled': rice_filled, + 'rice': rice, + 'roast_filled': roast_filled, + 'roast': roast, + 'rocket_filled': rocket_filled, + 'rocket': rocket, + 'rollback': rollback, + 'rollfront': rollfront, + 'root_list_filled': root_list_filled, + 'root_list': root_list, + 'rotate_locked_filled': rotate_locked_filled, + 'rotate_locked': rotate_locked, + 'rotate': rotate, + 'rotation': rotation, + 'round_filled': round_filled, + 'round': round, + 'router_wave_filled': router_wave_filled, + 'router_wave': router_wave, + 'rss': rss, + 'ruler_filled': ruler_filled, + 'ruler': ruler, + 'sailing_hotel_filled': sailing_hotel_filled, + 'sailing_hotel': sailing_hotel, + 'sandwich_filled': sandwich_filled, + 'sandwich': sandwich, + 'saturation_filled': saturation_filled, + 'saturation': saturation, + 'sausage_filled': sausage_filled, + 'sausage': sausage, + 'save_filled': save_filled, + 'save': save, + 'saving_pot_filled': saving_pot_filled, + 'saving_pot': saving_pot, + 'scan': scan, + 'screen_4k_filled': screen_4k_filled, + 'screen_4k': screen_4k, + 'screencast_filled': screencast_filled, + 'screencast': screencast, + 'screenshot': screenshot, + 'scroll_bar_filled': scroll_bar_filled, + 'scroll_bar': scroll_bar, + 'sd_card_1_filled': sd_card_1_filled, + 'sd_card_1': sd_card_1, + 'sd_card_filled': sd_card_filled, + 'sd_card': sd_card, + 'search_error_filled': search_error_filled, + 'search_error': search_error, + 'search_filled': search_filled, + 'search': search, + 'secured_filled': secured_filled, + 'secured': secured, + 'send_cancel_filled': send_cancel_filled, + 'send_cancel': send_cancel, + 'send_filled': send_filled, + 'send': send, + 'sensors_1': sensors_1, + 'sensors_2': sensors_2, + 'sensors_off': sensors_off, + 'sensors': sensors, + 'sequence_filled': sequence_filled, + 'sequence': sequence, + 'serenity_filled': serenity_filled, + 'serenity': serenity, + 'server_filled': server_filled, + 'server': server, + 'service_filled': service_filled, + 'service': service, + 'setting_1_filled': setting_1_filled, + 'setting_1': setting_1, + 'setting_filled': setting_filled, + 'setting': setting, + 'share_1_filled': share_1_filled, + 'share_1': share_1, + 'share_filled': share_filled, + 'share': share, + 'sharpness_filled': sharpness_filled, + 'sharpness': sharpness, + 'shield_error_filled': shield_error_filled, + 'shield_error': shield_error, + 'shimen_filled': shimen_filled, + 'shimen': shimen, + 'shop_1_filled': shop_1_filled, + 'shop_1': shop_1, + 'shop_2_filled': shop_2_filled, + 'shop_2': shop_2, + 'shop_3_filled': shop_3_filled, + 'shop_3': shop_3, + 'shop_4_filled': shop_4_filled, + 'shop_4': shop_4, + 'shop_5_filled': shop_5_filled, + 'shop_5': shop_5, + 'shop_filled': shop_filled, + 'shop': shop, + 'shrimp_filled': shrimp_filled, + 'shrimp': shrimp, + 'shrink_horizontal': shrink_horizontal, + 'shrink_vertical': shrink_vertical, + 'shutter_filled': shutter_filled, + 'shutter': shutter, + 'shutup_filled': shutup_filled, + 'shutup': shutup, + 'sim_card_1_filled': sim_card_1_filled, + 'sim_card_1': sim_card_1, + 'sim_card_2_filled': sim_card_2_filled, + 'sim_card_2': sim_card_2, + 'sim_card_filled': sim_card_filled, + 'sim_card': sim_card, + 'sinister_smile_filled': sinister_smile_filled, + 'sinister_smile': sinister_smile, + 'sip_filled': sip_filled, + 'sip': sip, + 'sitemap_filled': sitemap_filled, + 'sitemap': sitemap, + 'slash': slash, + 'sleep_filled': sleep_filled, + 'sleep': sleep, + 'slice_filled': slice_filled, + 'slice': slice, + 'slideshow_filled': slideshow_filled, + 'slideshow': slideshow, + 'smile_filled': smile_filled, + 'smile': smile, + 'sneer_filled': sneer_filled, + 'sneer': sneer, + 'snowflake': snowflake, + 'sonic': sonic, + 'sound_down_filled': sound_down_filled, + 'sound_down': sound_down, + 'sound_filled': sound_filled, + 'sound_high_filled': sound_high_filled, + 'sound_high': sound_high, + 'sound_low_filled': sound_low_filled, + 'sound_low': sound_low, + 'sound_mute_1_filled': sound_mute_1_filled, + 'sound_mute_1': sound_mute_1, + 'sound_mute_filled': sound_mute_filled, + 'sound_mute': sound_mute, + 'sound_up_filled': sound_up_filled, + 'sound_up': sound_up, + 'sound': sound, + 'space': space, + 'speechless_1_filled': speechless_1_filled, + 'speechless_1': speechless_1, + 'speechless_filled': speechless_filled, + 'speechless': speechless, + 'star_filled': star_filled, + 'star': star, + 'statue_of_jesus_filled': statue_of_jesus_filled, + 'statue_of_jesus': statue_of_jesus, + 'sticky_note_filled': sticky_note_filled, + 'sticky_note': sticky_note, + 'stop_circle_filled': stop_circle_filled, + 'stop_circle_stroke_filled': stop_circle_stroke_filled, + 'stop_circle_stroke': stop_circle_stroke, + 'stop_circle': stop_circle, + 'stop': stop, + 'store_filled': store_filled, + 'store': store, + 'street_road_1_filled': street_road_1_filled, + 'street_road_1': street_road_1, + 'street_road_filled': street_road_filled, + 'street_road': street_road, + 'subtitle_filled': subtitle_filled, + 'subtitle': subtitle, + 'subway_line_filled': subway_line_filled, + 'subway_line': subway_line, + 'sum': sum, + 'sun_fall_filled': sun_fall_filled, + 'sun_fall': sun_fall, + 'sun_rising_filled': sun_rising_filled, + 'sun_rising': sun_rising, + 'sunny_filled': sunny_filled, + 'sunny': sunny, + 'support_filled': support_filled, + 'support': support, + 'surprised_1_filled': surprised_1_filled, + 'surprised_1': surprised_1, + 'surprised_filled': surprised_filled, + 'surprised': surprised, + 'swap_left': swap_left, + 'swap_right': swap_right, + 'swap': swap, + 'swear_1_filled': swear_1_filled, + 'swear_1': swear_1, + 'swear_2_filled': swear_2_filled, + 'swear_2': swear_2, + 'system_2': system_2, + 'system_3_filled': system_3_filled, + 'system_3': system_3, + 'system_application_filled': system_application_filled, + 'system_application': system_application, + 'system_blocked_filled': system_blocked_filled, + 'system_blocked': system_blocked, + 'system_code_filled': system_code_filled, + 'system_code': system_code, + 'system_components_filled': system_components_filled, + 'system_components': system_components, + 'system_coordinate_filled': system_coordinate_filled, + 'system_coordinate': system_coordinate, + 'system_device_filled': system_device_filled, + 'system_device': system_device, + 'system_interface_filled': system_interface_filled, + 'system_interface': system_interface, + 'system_location_filled': system_location_filled, + 'system_location': system_location, + 'system_locked_filled': system_locked_filled, + 'system_locked': system_locked, + 'system_log_filled': system_log_filled, + 'system_log': system_log, + 'system_marked_filled': system_marked_filled, + 'system_marked': system_marked, + 'system_messages_filled': system_messages_filled, + 'system_messages': system_messages, + 'system_regulation_filled': system_regulation_filled, + 'system_regulation': system_regulation, + 'system_search_filled': system_search_filled, + 'system_search': system_search, + 'system_setting_filled': system_setting_filled, + 'system_setting': system_setting, + 'system_storage_filled': system_storage_filled, + 'system_storage': system_storage, + 'system_sum': system_sum, + 'system_unlocked_filled': system_unlocked_filled, + 'system_unlocked': system_unlocked, + 'tab_filled': tab_filled, + 'tab': tab, + 'table_1_filled': table_1_filled, + 'table_1': table_1, + 'table_2_filled': table_2_filled, + 'table_2': table_2, + 'table_add_filled': table_add_filled, + 'table_add': table_add, + 'table_filled': table_filled, + 'table_split_filled': table_split_filled, + 'table_split': table_split, + 'table': table, + 'tag_filled': tag_filled, + 'tag': tag, + 'tangerinr_filled': tangerinr_filled, + 'tangerinr': tangerinr, + 'tape_filled': tape_filled, + 'tape': tape, + 'task_1_filled': task_1_filled, + 'task_1': task_1, + 'task_add_1': task_add_1, + 'task_add_filled': task_add_filled, + 'task_add': task_add, + 'task_checked_1': task_checked_1, + 'task_checked_filled': task_checked_filled, + 'task_checked': task_checked, + 'task_double_filled': task_double_filled, + 'task_double': task_double, + 'task_error_filled': task_error_filled, + 'task_error': task_error, + 'task_filled': task_filled, + 'task_location_filled': task_location_filled, + 'task_location': task_location, + 'task_marked_filled': task_marked_filled, + 'task_marked': task_marked, + 'task_setting_filled': task_setting_filled, + 'task_setting': task_setting, + 'task_time_filled': task_time_filled, + 'task_time': task_time, + 'task_visible_filled': task_visible_filled, + 'task_visible': task_visible, + 'task': task, + 'tea_filled': tea_filled, + 'tea': tea, + 'teahouse_filled': teahouse_filled, + 'teahouse': teahouse, + 'template_filled': template_filled, + 'template': template, + 'temple_filled': temple_filled, + 'temple': temple, + 'terminal_rectangle_1_filled': terminal_rectangle_1_filled, + 'terminal_rectangle_1': terminal_rectangle_1, + 'terminal_rectangle_filled': terminal_rectangle_filled, + 'terminal_rectangle': terminal_rectangle, + 'terminal_window_filled': terminal_window_filled, + 'terminal_window': terminal_window, + 'terminal': terminal, + 'textbox_filled': textbox_filled, + 'textbox': textbox, + 'textformat_bold': textformat_bold, + 'textformat_color': textformat_color, + 'textformat_italic': textformat_italic, + 'textformat_strikethrough': textformat_strikethrough, + 'textformat_underline': textformat_underline, + 'textformat_wrap': textformat_wrap, + 'theaters_filled': theaters_filled, + 'theaters': theaters, + 'thumb_down_1_filled': thumb_down_1_filled, + 'thumb_down_1': thumb_down_1, + 'thumb_down_2_filled': thumb_down_2_filled, + 'thumb_down_2': thumb_down_2, + 'thumb_down_filled': thumb_down_filled, + 'thumb_down': thumb_down, + 'thumb_up_1_filled': thumb_up_1_filled, + 'thumb_up_1': thumb_up_1, + 'thumb_up_2_filled': thumb_up_2_filled, + 'thumb_up_2': thumb_up_2, + 'thumb_up_filled': thumb_up_filled, + 'thumb_up': thumb_up, + 'thunder': thunder, + 'thunderstorm_night_filled': thunderstorm_night_filled, + 'thunderstorm_night': thunderstorm_night, + 'thunderstorm_sunny_filled': thunderstorm_sunny_filled, + 'thunderstorm_sunny': thunderstorm_sunny, + 'thunderstorm': thunderstorm, + 'ticket_filled': ticket_filled, + 'ticket': ticket, + 'time_filled': time_filled, + 'time': time, + 'tips_double_filled': tips_double_filled, + 'tips_double': tips_double, + 'tips_filled': tips_filled, + 'tips': tips, + 'tomato_filled': tomato_filled, + 'tomato': tomato, + 'tools_circle_filled': tools_circle_filled, + 'tools_circle': tools_circle, + 'tools_filled': tools_filled, + 'tools': tools, + 'tornado': tornado, + 'tower_1_filled': tower_1_filled, + 'tower_1': tower_1, + 'tower_2_filled': tower_2_filled, + 'tower_2': tower_2, + 'tower_3_filled': tower_3_filled, + 'tower_3': tower_3, + 'tower_clock_filled': tower_clock_filled, + 'tower_clock': tower_clock, + 'tower_filled': tower_filled, + 'tower': tower, + 'town_filled': town_filled, + 'town': town, + 'traffic_events_filled': traffic_events_filled, + 'traffic_events': traffic_events, + 'traffic_filled': traffic_filled, + 'traffic': traffic, + 'transform_1_filled': transform_1_filled, + 'transform_1': transform_1, + 'transform_2': transform_2, + 'transform_3': transform_3, + 'transform_filled': transform_filled, + 'transform': transform, + 'translate_1': translate_1, + 'translate': translate, + 'tree_round_dot_filled': tree_round_dot_filled, + 'tree_round_dot_vertical_filled': tree_round_dot_vertical_filled, + 'tree_round_dot_vertical': tree_round_dot_vertical, + 'tree_round_dot': tree_round_dot, + 'tree_square_dot_filled': tree_square_dot_filled, + 'tree_square_dot_vertical_filled': tree_square_dot_vertical_filled, + 'tree_square_dot_vertical': tree_square_dot_vertical, + 'tree_square_dot': tree_square_dot, + 'trending_down': trending_down, + 'trending_up': trending_up, + 'tv_1_filled': tv_1_filled, + 'tv_1': tv_1, + 'tv_2_filled': tv_2_filled, + 'tv_2': tv_2, + 'tv_filled': tv_filled, + 'tv': tv, + 'typography_filled': typography_filled, + 'typography': typography, + 'uncomfortable_1_filled': uncomfortable_1_filled, + 'uncomfortable_1': uncomfortable_1, + 'uncomfortable_2_filled': uncomfortable_2_filled, + 'uncomfortable_2': uncomfortable_2, + 'uncomfortable_filled': uncomfortable_filled, + 'uncomfortable': uncomfortable, + 'undertake_delivery_filled': undertake_delivery_filled, + 'undertake_delivery': undertake_delivery, + 'undertake_environment_protection_filled': undertake_environment_protection_filled, + 'undertake_environment_protection': undertake_environment_protection, + 'undertake_filled': undertake_filled, + 'undertake_hold_up_filled': undertake_hold_up_filled, + 'undertake_hold_up': undertake_hold_up, + 'undertake_transaction_filled': undertake_transaction_filled, + 'undertake_transaction': undertake_transaction, + 'undertake': undertake, + 'unfold_less': unfold_less, + 'unfold_more': unfold_more, + 'unhappy_1_filled': unhappy_1_filled, + 'unhappy_1': unhappy_1, + 'unhappy_filled': unhappy_filled, + 'unhappy': unhappy, + 'uninstall_filled': uninstall_filled, + 'uninstall': uninstall, + 'upload_1': upload_1, + 'upload': upload, + 'upscale': upscale, + 'usb_filled': usb_filled, + 'usb': usb, + 'user_1_filled': user_1_filled, + 'user_1': user_1, + 'user_add_filled': user_add_filled, + 'user_add': user_add, + 'user_arrow_down_filled': user_arrow_down_filled, + 'user_arrow_down': user_arrow_down, + 'user_arrow_left_filled': user_arrow_left_filled, + 'user_arrow_left': user_arrow_left, + 'user_arrow_right_filled': user_arrow_right_filled, + 'user_arrow_right': user_arrow_right, + 'user_arrow_up_filled': user_arrow_up_filled, + 'user_arrow_up': user_arrow_up, + 'user_avatar_filled': user_avatar_filled, + 'user_avatar': user_avatar, + 'user_blocked_filled': user_blocked_filled, + 'user_blocked': user_blocked, + 'user_business_filled': user_business_filled, + 'user_business': user_business, + 'user_checked_1_filled': user_checked_1_filled, + 'user_checked_1': user_checked_1, + 'user_checked_filled': user_checked_filled, + 'user_checked': user_checked, + 'user_circle_filled': user_circle_filled, + 'user_circle': user_circle, + 'user_clear_filled': user_clear_filled, + 'user_clear': user_clear, + 'user_error_1_filled': user_error_1_filled, + 'user_error_1': user_error_1, + 'user_filled': user_filled, + 'user_invisible_filled': user_invisible_filled, + 'user_invisible': user_invisible, + 'user_list_filled': user_list_filled, + 'user_list': user_list, + 'user_locked_filled': user_locked_filled, + 'user_locked': user_locked, + 'user_marked_filled': user_marked_filled, + 'user_marked': user_marked, + 'user_password_filled': user_password_filled, + 'user_password': user_password, + 'user_safety_filled': user_safety_filled, + 'user_safety': user_safety, + 'user_search_filled': user_search_filled, + 'user_search': user_search, + 'user_setting_filled': user_setting_filled, + 'user_setting': user_setting, + 'user_talk_1_filled': user_talk_1_filled, + 'user_talk_1': user_talk_1, + 'user_talk_filled': user_talk_filled, + 'user_talk_off_1_filled': user_talk_off_1_filled, + 'user_talk_off_1': user_talk_off_1, + 'user_talk': user_talk, + 'user_time_filled': user_time_filled, + 'user_time': user_time, + 'user_transmit_filled': user_transmit_filled, + 'user_transmit': user_transmit, + 'user_unknown_filled': user_unknown_filled, + 'user_unknown': user_unknown, + 'user_unlocked_filled': user_unlocked_filled, + 'user_unlocked': user_unlocked, + 'user_vip_filled': user_vip_filled, + 'user_vip': user_vip, + 'user_visible_filled': user_visible_filled, + 'user_visible': user_visible, + 'user': user, + 'usercase_filled': usercase_filled, + 'usercase_link_filled': usercase_link_filled, + 'usercase_link': usercase_link, + 'usercase': usercase, + 'usergroup_add_filled': usergroup_add_filled, + 'usergroup_add': usergroup_add, + 'usergroup_clear_filled': usergroup_clear_filled, + 'usergroup_clear': usergroup_clear, + 'usergroup_filled': usergroup_filled, + 'usergroup': usergroup, + 'vehicle_filled': vehicle_filled, + 'vehicle': vehicle, + 'verified_filled': verified_filled, + 'verified': verified, + 'verify_filled': verify_filled, + 'verify': verify, + 'vertical_filled': vertical_filled, + 'vertical': vertical, + 'video_camera_1_filled': video_camera_1_filled, + 'video_camera_1': video_camera_1, + 'video_camera_2_filled': video_camera_2_filled, + 'video_camera_2': video_camera_2, + 'video_camera_3_filled': video_camera_3_filled, + 'video_camera_3': video_camera_3, + 'video_camera_dollar_filled': video_camera_dollar_filled, + 'video_camera_dollar': video_camera_dollar, + 'video_camera_filled': video_camera_filled, + 'video_camera_minus_filled': video_camera_minus_filled, + 'video_camera_minus': video_camera_minus, + 'video_camera_music_filled': video_camera_music_filled, + 'video_camera_music': video_camera_music, + 'video_camera_off_filled': video_camera_off_filled, + 'video_camera_off': video_camera_off, + 'video_camera': video_camera, + 'video_filled': video_filled, + 'video_library_filled': video_library_filled, + 'video_library': video_library, + 'video': video, + 'view_agenda_filled': view_agenda_filled, + 'view_agenda': view_agenda, + 'view_column': view_column, + 'view_in_ar_filled': view_in_ar_filled, + 'view_in_ar': view_in_ar, + 'view_list': view_list, + 'view_module_filled': view_module_filled, + 'view_module': view_module, + 'visual_recognition_filled': visual_recognition_filled, + 'visual_recognition': visual_recognition, + 'wallet_filled': wallet_filled, + 'wallet': wallet, + 'watch_filled': watch_filled, + 'watch': watch, + 'watermelon_filled': watermelon_filled, + 'watermelon': watermelon, + 'wave_bye_filled': wave_bye_filled, + 'wave_bye': wave_bye, + 'wave_left_filled': wave_left_filled, + 'wave_left': wave_left, + 'wave_right_filled': wave_right_filled, + 'wave_right': wave_right, + 'wealth_1_filled': wealth_1_filled, + 'wealth_1': wealth_1, + 'wealth_filled': wealth_filled, + 'wealth': wealth, + 'widget_filled': widget_filled, + 'widget': widget, + 'wifi_1_filled': wifi_1_filled, + 'wifi_1': wifi_1, + 'wifi_off_1_filled': wifi_off_1_filled, + 'wifi_off_1': wifi_off_1, + 'wifi_off': wifi_off, + 'wifi': wifi, + 'window_1_filled': window_1_filled, + 'window_1': window_1, + 'window_filled': window_filled, + 'window': window, + 'windy_rain': windy_rain, + 'windy': windy, + 'wink_filled': wink_filled, + 'wink': wink, + 'work_filled': work_filled, + 'work_history_filled': work_history_filled, + 'work_history': work_history, + 'work_off_filled': work_off_filled, + 'work_off': work_off, + 'work': work, + 'wry_smile_filled': wry_smile_filled, + 'wry_smile': wry_smile, + 'zoom_in_filled': zoom_in_filled, + 'zoom_in': zoom_in, + 'zoom_out_filled': zoom_out_filled, + 'zoom_out': zoom_out, + + }; +} + diff --git a/tdesign-component/lib/src/components/image/image_widget.dart b/tdesign-component/lib/src/components/image/image_widget.dart new file mode 100644 index 000000000..5a15ef9c5 --- /dev/null +++ b/tdesign-component/lib/src/components/image/image_widget.dart @@ -0,0 +1,370 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; +import 'dart:io'; + +///封装图片加载控件,增加图片加载失败时加载默认图片 +class ImageWidget extends StatefulWidget { + /// 图片地址 + final String? src; + + /// 本地图片地址 + final String? assetUrl; + + /// 图片文件路径 + final File? imageFile; + + /// 图片宽度 + final double? width; + + /// 图片高度 + final double? height; + + /// 加载错误时展示Widget + final Widget? errorWidget; + + /// 加载中展示Widget + final Widget? loadingWidget; + + /// 适配样式 + final BoxFit fit; + + /// 以下系统Image属性,释义请参考系统[Image]中注释 + final ImageProvider image; + + final ImageFrameBuilder? frameBuilder; + + final ImageLoadingBuilder? loadingBuilder; + + final ImageErrorWidgetBuilder? errorBuilder; + + final Color? color; + + final Animation? opacity; + + final FilterQuality filterQuality; + + final BlendMode? colorBlendMode; + + final AlignmentGeometry alignment; + + final ImageRepeat repeat; + + final Rect? centerSlice; + + final bool matchTextDirection; + + final bool gaplessPlayback; + + final String? semanticLabel; + + final bool excludeFromSemantics; + + final bool isAntiAlias; + + final int? cacheWidth; + + final int? cacheHeight; + + const ImageWidget( + {Key? key, + required this.image, + this.frameBuilder, + this.loadingBuilder, + this.errorBuilder, + this.semanticLabel, + this.excludeFromSemantics = false, + this.width, + this.height, + this.color, + this.opacity, + this.colorBlendMode, + required this.fit, + this.alignment = Alignment.center, + this.repeat = ImageRepeat.noRepeat, + this.centerSlice, + this.matchTextDirection = false, + this.gaplessPlayback = false, + this.isAntiAlias = false, + this.filterQuality = FilterQuality.low, + required this.src, + this.errorWidget, + this.loadingWidget, + this.cacheWidth, + this.cacheHeight, + this.assetUrl, + this.imageFile}) + : super(key: key); + + ImageWidget.network(this.src, + {Key? key, + this.width, + this.height, + double scale = 1.0, + this.errorWidget, + this.fit = BoxFit.none, + this.loadingWidget, + this.frameBuilder, + this.loadingBuilder, + this.errorBuilder, + this.semanticLabel, + this.excludeFromSemantics = false, + this.color, + this.opacity, + this.colorBlendMode, + this.alignment = Alignment.center, + this.repeat = ImageRepeat.noRepeat, + this.centerSlice, + this.matchTextDirection = false, + this.gaplessPlayback = false, + this.filterQuality = FilterQuality.low, + this.isAntiAlias = false, + Map? headers, + this.cacheWidth, + this.assetUrl, + this.cacheHeight, + this.imageFile}) + : image = ResizeImage.resizeIfNeeded( + cacheWidth, cacheHeight, NetworkImage(src ?? '', scale: scale, headers: headers)), + assert(cacheWidth == null || cacheWidth > 0), + assert(cacheHeight == null || cacheHeight > 0), + super(key: key); + + ImageWidget.asset(this.assetUrl, + {Key? key, + AssetBundle? bundle, + this.frameBuilder, + this.errorBuilder, + this.semanticLabel, + this.excludeFromSemantics = false, + double? scale, + this.width, + this.height, + this.color, + this.opacity, + this.colorBlendMode, + this.fit = BoxFit.none, + this.alignment = Alignment.center, + this.repeat = ImageRepeat.noRepeat, + this.centerSlice, + this.matchTextDirection = false, + this.gaplessPlayback = false, + this.isAntiAlias = false, + String? package, + this.filterQuality = FilterQuality.low, + this.cacheWidth, + this.cacheHeight, + this.src, + this.errorWidget, + this.loadingWidget, + this.imageFile}) + : image = ResizeImage.resizeIfNeeded( + cacheWidth, + cacheHeight, + scale != null + ? ExactAssetImage(assetUrl ?? '', bundle: bundle, scale: scale, package: package) + : AssetImage(assetUrl ?? '', bundle: bundle, package: package), + ), + loadingBuilder = null, + assert(cacheWidth == null || cacheWidth > 0), + assert(cacheHeight == null || cacheHeight > 0), + super(key: key); + + ImageWidget.file( + this.imageFile, { + Key? key, + double scale = 1.0, + this.frameBuilder, + this.errorBuilder, + this.semanticLabel, + this.excludeFromSemantics = false, + this.width, + this.height, + this.color, + this.opacity, + this.colorBlendMode, + this.fit = BoxFit.none, + this.alignment = Alignment.center, + this.repeat = ImageRepeat.noRepeat, + this.centerSlice, + this.matchTextDirection = false, + this.gaplessPlayback = false, + this.isAntiAlias = false, + this.filterQuality = FilterQuality.low, + this.assetUrl, + this.cacheWidth, + this.cacheHeight, + this.errorWidget, + this.loadingWidget, + this.src, + }) : image = ResizeImage.resizeIfNeeded(cacheWidth, cacheHeight, FileImage(imageFile!, scale: scale)), + loadingBuilder = null, + assert(alignment != null), + assert(repeat != null), + assert(filterQuality != null), + assert(matchTextDirection != null), + assert(cacheWidth == null || cacheWidth > 0), + assert(cacheHeight == null || cacheHeight > 0), + assert(isAntiAlias != null), + super(key: key); + @override + State createState() { + return _StateImageWidget(); + } +} + +class _StateImageWidget extends State { + late Image _image; + late ImageStream _resolve; + late ImageStreamListener _listener; + bool error = false; + bool loading = true; + + @override + void didUpdateWidget(covariant ImageWidget oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.src != widget.src || oldWidget.assetUrl != widget.assetUrl) { + initImage(); + } + } + + void initImage() { + _image = widget.imageFile == null + ? widget.assetUrl == null + ? Image.network( + widget.src ?? '', + width: widget.width, + height: widget.height, + fit: widget.fit, + color: widget.color, + frameBuilder: widget.frameBuilder, + loadingBuilder: widget.loadingBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheWidth: widget.cacheWidth, + cacheHeight: widget.cacheHeight, + ) + : Image.asset( + widget.assetUrl ?? '', + width: widget.width, + height: widget.height, + fit: widget.fit, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheWidth: widget.cacheWidth, + cacheHeight: widget.cacheHeight, + ) + : Image.file( + widget.imageFile!, + width: widget.width, + height: widget.height, + fit: widget.fit, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheWidth: widget.cacheWidth, + cacheHeight: widget.cacheHeight, + ); + _resolve = _image.image.resolve(const ImageConfiguration()); + _listener = ImageStreamListener((_, __) { + /// 加载成功 + if (mounted) { + setState(() { + loading = false; + error = false; + }); + } + }, onChunk: (ImageChunkEvent event) { + /// 加载中 + if (loading == false) { + if (mounted) { + setState(() { + loading = true; + error = false; + }); + } + } + }, onError: (dynamic exception, StackTrace? stackTrace) { + /// 加载失败 + if (error == false) { + setState(() { + error = true; + loading = false; + }); + } + }); + _resolve.addListener(_listener); + } + + @override + void initState() { + super.initState(); + initImage(); + } + + @override + Widget build(BuildContext context) { + if (error == false && loading == true) { + return Container( + alignment: widget.alignment, + color: widget.color ?? TDTheme.of(context).grayColor2, + child: widget.loadingWidget ?? + Icon( + TDIcons.ellipsis, + size: 22, + color: TDTheme.of(context).fontGyColor3, + )); + } + if (error == true && loading == false) { + return Container( + alignment: widget.alignment, + color: widget.color ?? TDTheme.of(context).grayColor2, + child: widget.errorWidget ?? + Icon( + TDIcons.close, + size: 22, + color: TDTheme.of(context).fontGyColor3, + ), + ); + } + if (loading == false && error == false) { + return _image; + } + return Container(); + } + + @override + void dispose() { + super.dispose(); + _resolve.removeListener(_listener); + } +} diff --git a/tdesign-component/lib/src/components/image/td_image.dart b/tdesign-component/lib/src/components/image/td_image.dart new file mode 100644 index 000000000..9eaa14f4b --- /dev/null +++ b/tdesign-component/lib/src/components/image/td_image.dart @@ -0,0 +1,667 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; +import '../../util/string_util.dart'; +import 'dart:io'; + +enum TDImageType { + /// 裁剪 + clip, + + /// 适应高 + fitHeight, + + /// 适应宽 + fitWidth, + + /// 拉伸 + stretch, + + /// 方形, + square, + + /// 圆角方形 + roundedSquare, + + /// 圆形 + circle, +} + +class TDImage extends StatefulWidget { + const TDImage({ + this.imgUrl, + Key? key, + this.type = TDImageType.roundedSquare, + this.errorWidget, + this.loadingWidget, + this.width, + this.height, + this.fit, + this.frameBuilder, + this.loadingBuilder, + this.errorBuilder, + this.semanticLabel, + this.excludeFromSemantics = false, + this.color, + this.opacity, + this.colorBlendMode, + this.alignment = Alignment.center, + this.repeat = ImageRepeat.noRepeat, + this.centerSlice, + this.matchTextDirection = false, + this.gaplessPlayback = false, + this.isAntiAlias = false, + this.filterQuality = FilterQuality.low, + this.cacheHeight, + this.cacheWidth, + this.assetUrl, + this.imageFile, + }) : super(key: key); + + /// 图片地址 + final String? imgUrl; + + /// 本地素材地址 + final String? assetUrl; + + /// 图片文件路径 + final File? imageFile; + + /// 图片类型 + final TDImageType type; + + /// 加载自定义提示 + final Widget? loadingWidget; + + /// 失败自定义提示 + final Widget? errorWidget; + + /// 自定义宽 + final double? width; + + /// 自定义高 + final double? height; + + /// 适配样式 + final BoxFit? fit; + + /// 以下系统Image属性,释义请参考系统[Image]中注释 + + final ImageFrameBuilder? frameBuilder; + + final ImageLoadingBuilder? loadingBuilder; + + final ImageErrorWidgetBuilder? errorBuilder; + + final Color? color; + + final Animation? opacity; + + final FilterQuality filterQuality; + + final BlendMode? colorBlendMode; + + final AlignmentGeometry alignment; + + final ImageRepeat repeat; + + final Rect? centerSlice; + + final bool matchTextDirection; + + final bool gaplessPlayback; + + final String? semanticLabel; + + final bool excludeFromSemantics; + + final bool isAntiAlias; + + final int? cacheHeight; + + final int? cacheWidth; + + @override + State createState() => _TDImageState(); +} + +class _TDImageState extends State { + @override + Widget build(BuildContext context) { + switch (widget.type) { + case TDImageType.clip: + return widget.imageFile == null + ? (widget.assetUrl == null + ? ImageWidget.network( + widget.imgUrl, + height: widget.height ?? 72, + width: widget.width ?? 72, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.none, + color: widget.color, + frameBuilder: widget.frameBuilder, + loadingBuilder: widget.loadingBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + ) + : ImageWidget.asset( + widget.assetUrl!, + width: widget.width ?? 72, + height: widget.height ?? 72, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.none, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + )) + : ImageWidget.file( + widget.imageFile, + width: widget.width ?? 72, + height: widget.height ?? 72, + fit: widget.fit ?? BoxFit.none, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheWidth: widget.cacheWidth, + cacheHeight: widget.cacheHeight, + ); + case TDImageType.fitHeight: + return widget.imageFile == null + ? (widget.assetUrl == null + ? ImageWidget.network( + widget.imgUrl, + height: widget.height, + width: widget.width, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.fitHeight, + color: widget.color, + frameBuilder: widget.frameBuilder, + loadingBuilder: widget.loadingBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + ) + : ImageWidget.asset( + widget.assetUrl!, + width: widget.width, + height: widget.height, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.fitHeight, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + )) + : ImageWidget.file( + widget.imageFile, + width: widget.width, + height: widget.height, + fit: widget.fit ?? BoxFit.fitHeight, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheWidth: widget.cacheWidth, + cacheHeight: widget.cacheHeight, + ); + case TDImageType.stretch: + return widget.imageFile == null + ? (widget.assetUrl == null + ? ConstrainedBox( + constraints: BoxConstraints(maxHeight: widget.height ?? 72, maxWidth: widget.width ?? 72), + child: ImageWidget.network( + widget.imgUrl, + height: widget.height ?? 72, + width: widget.width ?? 72, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.fill, + color: widget.color, + frameBuilder: widget.frameBuilder, + loadingBuilder: widget.loadingBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + ), + ) + : ConstrainedBox( + constraints: BoxConstraints(maxHeight: widget.height ?? 72, maxWidth: widget.width ?? 72), + child: ImageWidget.asset( + widget.assetUrl!, + width: widget.width ?? 72, + height: widget.height ?? 72, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.fill, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + ), + )) + : ImageWidget.file( + widget.imageFile, + width: widget.width ?? 72, + height: widget.height ?? 72, + fit: widget.fit ?? BoxFit.fill, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheWidth: widget.cacheWidth, + cacheHeight: widget.cacheHeight, + ); + case TDImageType.square: + return widget.imageFile == null + ? (widget.assetUrl == null + ? ImageWidget.network( + widget.imgUrl, + height: widget.height ?? 72, + width: widget.width ?? 72, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.cover, + color: widget.color, + frameBuilder: widget.frameBuilder, + loadingBuilder: widget.loadingBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + ) + : ImageWidget.asset( + widget.assetUrl!, + width: widget.width ?? 72, + height: widget.height ?? 72, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.cover, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + )) + : ImageWidget.file( + widget.imageFile, + width: widget.width ?? 72, + height: widget.height ?? 72, + fit: widget.fit ?? BoxFit.cover, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheWidth: widget.cacheWidth, + cacheHeight: widget.cacheHeight, + ); + case TDImageType.roundedSquare: + return Container( + height: widget.height ?? 72, + width: widget.width ?? 72, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault)), + child: widget.imageFile == null + ? (widget.assetUrl == null + ? ImageWidget.network( + widget.imgUrl, + height: widget.height ?? 72, + width: widget.width ?? 72, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.cover, + color: widget.color, + frameBuilder: widget.frameBuilder, + loadingBuilder: widget.loadingBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + ) + : ImageWidget.asset( + widget.assetUrl!, + width: widget.width ?? 72, + height: widget.height ?? 72, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.cover, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + )) + : ImageWidget.file( + widget.imageFile, + width: widget.width ?? 72, + height: widget.height ?? 72, + fit: widget.fit ?? BoxFit.cover, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheWidth: widget.cacheWidth, + cacheHeight: widget.cacheHeight, + )); + case TDImageType.circle: + return Container( + height: widget.height ?? 72, + width: widget.width ?? 72, + clipBehavior: Clip.hardEdge, + decoration: const BoxDecoration(shape: BoxShape.circle), + child: widget.imageFile == null + ? (widget.assetUrl == null + ? ImageWidget.network( + widget.imgUrl, + height: widget.height ?? 72, + width: widget.width ?? 72, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.cover, + color: widget.color, + frameBuilder: widget.frameBuilder, + loadingBuilder: widget.loadingBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + ) + : ImageWidget.asset( + widget.assetUrl!, + width: widget.width ?? 72, + height: widget.height ?? 72, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.cover, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + )) + : ImageWidget.file( + widget.imageFile!, + width: widget.width ?? 72, + height: widget.height ?? 72, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.cover, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + )); + case TDImageType.fitWidth: + return widget.imageFile == null + ? (widget.assetUrl == null + ? ImageWidget.network( + widget.imgUrl, + height: widget.height, + width: widget.width, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.fitWidth, + color: widget.color, + frameBuilder: widget.frameBuilder, + loadingBuilder: widget.loadingBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + ) + : ImageWidget.asset( + widget.assetUrl!, + width: widget.width, + height: widget.height, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.fitWidth, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + )) + : ImageWidget.file( + widget.imageFile!, + width: widget.width, + height: widget.height, + errorWidget: widget.errorWidget, + loadingWidget: widget.loadingWidget, + fit: widget.fit ?? BoxFit.fitWidth, + color: widget.color, + frameBuilder: widget.frameBuilder, + errorBuilder: widget.errorBuilder, + semanticLabel: widget.semanticLabel, + excludeFromSemantics: widget.excludeFromSemantics, + opacity: widget.opacity, + colorBlendMode: widget.colorBlendMode, + alignment: widget.alignment, + repeat: widget.repeat, + centerSlice: widget.centerSlice, + matchTextDirection: widget.matchTextDirection, + gaplessPlayback: widget.gaplessPlayback, + filterQuality: widget.filterQuality, + isAntiAlias: widget.isAntiAlias, + cacheHeight: widget.cacheHeight, + cacheWidth: widget.cacheWidth, + ); + } + } +} diff --git a/tdesign-component/lib/src/components/image_viewer/td_image_viewer.dart b/tdesign-component/lib/src/components/image_viewer/td_image_viewer.dart new file mode 100644 index 000000000..109147256 --- /dev/null +++ b/tdesign-component/lib/src/components/image_viewer/td_image_viewer.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; + +import '../../theme/td_colors.dart'; +import '../../theme/td_theme.dart'; +import '../popup/td_popup_route.dart'; +import 'td_image_viewer_widget.dart'; + +/// 图片预览工具 +class TDImageViewer { + + /// 显示图片预览 + static void showImageViewer({ + required BuildContext context, + required List images, + List? labels, + bool? closeBtn = true, + bool? deleteBtn = false, + bool? showIndex = false, + bool? loop = false, + bool? autoplay = false, + int? duration, + Color? bgColor, + Color? navBarBgColor, + Color? iconColor, + TextStyle? labelStyle, + TextStyle? indexStyle, + Color? modalBarrierColor, + bool? barrierDismissible, + int? defaultIndex, + double? width, + double? height, + OnIndexChange? onIndexChange, + OnClose? onClose, + OnDelete? onDelete, + bool? ignoreDeleteError, + OnImageTap? onTap, + OnLongPress? onLongPress, + LeftItemBuilder? leftItemBuilder, + RightItemBuilder? rightItemBuilder, + }) { + modalBarrierColor ??= TDTheme.of(context).fontGyColor1; + showDialog( + context: context, + barrierDismissible: barrierDismissible ?? false, + barrierColor: modalBarrierColor, + useSafeArea: false, + builder: (context) { + return TDImageViewerWidget( + images: images, + labels: labels, + closeBtn: closeBtn, + deleteBtn: deleteBtn, + showIndex: showIndex, + loop: loop, + autoplay: autoplay, + duration: duration, + bgColor: bgColor, + navBarBgColor: navBarBgColor, + iconColor: iconColor, + labelStyle: labelStyle, + indexStyle: indexStyle, + defaultIndex: defaultIndex, + onIndexChange: onIndexChange, + width: width, + height: height, + onClose: onClose, + onDelete: onDelete, + ignoreDeleteError: ignoreDeleteError, + onTap: onTap, + onLongPress: onLongPress, + leftItemBuilder: leftItemBuilder, + rightItemBuilder: rightItemBuilder, + ); + }, + ); + } +} diff --git a/tdesign-component/lib/src/components/image_viewer/td_image_viewer_widget.dart b/tdesign-component/lib/src/components/image_viewer/td_image_viewer_widget.dart new file mode 100644 index 000000000..38b9f3ce8 --- /dev/null +++ b/tdesign-component/lib/src/components/image_viewer/td_image_viewer_widget.dart @@ -0,0 +1,340 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart'; + +import '../../../tdesign_flutter.dart'; +import '../navbar/td_nav_bar.dart'; + +typedef OnIndexChange = Function(int index); +typedef OnClose = Function(int index); +typedef OnDelete = Function(int index); +typedef OnImageTap = Function(int index); +typedef OnLongPress = Function(int index); +typedef LeftItemBuilder = Widget Function(BuildContext context, int index); +typedef RightItemBuilder = Widget Function(BuildContext context, int index); + +class TDImageViewerWidget extends StatefulWidget { + const TDImageViewerWidget({ + Key? key, + this.closeBtn, + this.deleteBtn, + required this.images, + this.labels, + this.showIndex, + this.loop, + this.autoplay, + this.duration, + this.bgColor, + this.navBarBgColor, + this.iconColor, + this.labelStyle, + this.indexStyle, + this.defaultIndex, + this.onIndexChange, + this.width, + this.height, + this.onClose, + this.onDelete, + this.ignoreDeleteError = false, + this.onTap, + this.onLongPress, + this.leftItemBuilder, + this.rightItemBuilder, + }) : super(key: key); + + /// 是否展示关闭按钮 + final bool? closeBtn; + + /// 是否显示删除操作 + final bool? deleteBtn; + + /// 图片数组 + final List images; + + /// 图片描述 + final List? labels; + + /// 是否显示页码 + final bool? showIndex; + + /// 图片是否循环 + final bool? loop; + + /// 图片轮播是否自动播放 + final bool? autoplay; + + /// 自动播放间隔 + final int? duration; + + /// 背景色 + final Color? bgColor; + + /// 导航栏背景色 + final Color? navBarBgColor; + + /// 图标颜色 + final Color? iconColor; + + /// label文字样式 + final TextStyle? labelStyle; + + /// 页码样式 + final TextStyle? indexStyle; + + /// 默认预览图片所在的下标 + final int? defaultIndex; + + /// 预览图片切换回调 + final OnIndexChange? onIndexChange; + + /// 关闭点击 + final OnClose? onClose; + + /// 删除点击 + final OnDelete? onDelete; + + /// 是否忽略单张图片删除错误提示 + final bool? ignoreDeleteError; + + /// 点击图片 + final OnImageTap? onTap; + + /// 长按图片 + final OnLongPress? onLongPress; + + /// 图片宽度 + final double? width; + + /// 图片高度 + final double? height; + + /// 左侧自定义操作 + final LeftItemBuilder? leftItemBuilder; + + /// 右侧自定义操作 + final RightItemBuilder? rightItemBuilder; + + @override + State createState() { + return _TDImageViewerWidgetState(); + } +} + +class _TDImageViewerWidgetState extends State { + int _index = 1; + + @override + void initState() { + super.initState(); + if(widget.images.isEmpty) { + throw FlutterError('images must not be empty'); + } + if((widget.defaultIndex ?? 0) > widget.images.length - 1) { + throw FlutterError('defaultIndex must be less than images.length'); + } + if(widget.labels != null && widget.images.length != widget.labels!.length) { + throw FlutterError('labels.length must be equals images.length'); + } + _index = (widget.defaultIndex ?? 0) + 1; + } + + Widget _getImage(dynamic image) { + var size = MediaQuery.of(context).size; + var boxFit = ((widget.width != null) || (widget.height != null)) + ? BoxFit.fill + : BoxFit.fitWidth; + var horizontal = + widget.width != null ? (size.width - (widget.width ?? 0)) / 2 : 0.0; + var vertical = + widget.height != null ? (size.height - (widget.height ?? 0)) / 2 : 0.0; + var margin = + EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical); + if (image is File) { + return Container( + margin: margin, + child: TDImage( + imageFile: image, + fit: boxFit, + type: TDImageType.fitWidth, + ), + ); + } + if (image is String) { + if (image.startsWith('http')) { + return Container( + margin: margin, + child: TDImage( + imgUrl: image, + fit: boxFit, + type: TDImageType.fitWidth, + loadingWidget: Container( + width: size.width, + height: size.height, + color: TDTheme.of(context).fontGyColor1, + child: Center( + child: TDLoading( + icon: TDLoadingIcon.circle, + size: TDLoadingSize.large, + iconColor: TDTheme.of(context).brandNormalColor, + ), + ), + ), + ), + ); + } + return Container( + margin: margin, + child: TDImage( + assetUrl: image, + fit: boxFit, + type: TDImageType.fitWidth, + ), + ); + } + throw FlutterError('image ${image} type is not supported'); + } + + Widget _getPageTitle() { + if(widget.labels != null) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Visibility( + visible: (widget.labels![_index - 1]) != '', + child: Text(widget.labels![_index - 1], + textAlign: TextAlign.center, + style: widget.labelStyle ?? TextStyle(color: TDTheme.of(context).whiteColor1), + ), + ), + Visibility( + visible: widget.showIndex ?? false, + child: Text('$_index / ${widget.images.length}', + textAlign: TextAlign.center, + style: widget.indexStyle ?? TextStyle(color: TDTheme.of(context).brandClickColor, fontSize: 10), + ), + ) + ], + ); + } + return Text( + (widget.showIndex ?? false) + ? '$_index / ${widget.images.length}' + : '', + textAlign: TextAlign.center, + style: widget.indexStyle ?? TextStyle(color: TDTheme.of(context).whiteColor1), + ); + } + + Widget _getLeft() { + if(widget.leftItemBuilder != null) { + return widget.leftItemBuilder!(context, _index - 1); + } + return GestureDetector( + onTap: () { + if (widget.onClose != null) { + widget.onClose!.call(_index - 1); + } else { + Navigator.of(context).pop(); + } + }, + child: Icon( + TDIcons.close, + color: widget.iconColor ?? TDTheme.of(context).whiteColor1, + ), + ); + } + + Widget _getRight() { + if(widget.rightItemBuilder != null) { + return widget.rightItemBuilder!(context, _index - 1); + } + return Visibility( + visible: widget.deleteBtn ?? false, + child: GestureDetector( + onTap: () { + if(widget.images.length == 1 && !(widget.ignoreDeleteError ?? false)) { + throw FlutterError('images must not be empty'); + } + widget.images.removeAt(_index - 1); + widget.onDelete?.call(_index - 1); + setState(() { + if(_index > 1) { + _index--; + } + }); + }, + child: Icon( + TDIcons.delete, + color: widget.iconColor ?? TDTheme.of(context).whiteColor1, + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + var media = MediaQuery.of(context); + var safeAreaHeight = media.padding.top; + return Stack( + children: [ + Positioned( + top: 0, + bottom: 0, + left: 0, + right: 0, + child: Container( + color: widget.bgColor ?? TDTheme.of(context).fontGyColor1, + ), + ), + Positioned( + top: safeAreaHeight, + bottom: 0, + left: 0, + right: 0, + child: Swiper( + index: _index - 1, + loop: widget.loop ?? true, + autoplay: widget.autoplay ?? false, + duration: widget.duration ?? kDefaultAutoplayTransactionDuration, + itemBuilder: (BuildContext context, int index) { + var image = widget.images[index]; + return GestureDetector( + onTap: () => widget.onTap?.call(index), + onLongPress: () => widget.onLongPress?.call(index), + child: _getImage(image), + ); + }, + itemCount: widget.images.length, + onIndexChanged: (index) { + if ((widget.showIndex ?? false) || widget.labels != null) { + setState(() { + _index = index + 1; + }); + } + widget.onIndexChange?.call(index); + }, + ), + ), + SafeArea( + child: Container( + color: widget.navBarBgColor ?? const Color(0x66000000), + height: 44, + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + _getLeft(), + Expanded( + flex: 1, + child: _getPageTitle(), + ), + _getRight(), + ], + ), + ), + ), + ], + ); + } +} diff --git a/tdesign-component/lib/src/components/indexes/sticky_header/layout_builder_hook.dart b/tdesign-component/lib/src/components/indexes/sticky_header/layout_builder_hook.dart new file mode 100644 index 000000000..974b4233e --- /dev/null +++ b/tdesign-component/lib/src/components/indexes/sticky_header/layout_builder_hook.dart @@ -0,0 +1,367 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + + +/// The signature of the [LayoutBuilder] builder function. +typedef LayoutWidgetBuilder = Widget Function(BuildContext context, BoxConstraints constraints); + +/// An abstract superclass for widgets that defer their building until layout. +/// +/// Similar to the [Builder] widget except that the framework calls the [builder] +/// function at layout time and provides the constraints that this widget should +/// adhere to. This is useful when the parent constrains the child's size and layout, +/// and doesn't depend on the child's intrinsic size. +/// +/// {@template flutter.widgets.ConstrainedLayoutBuilder} +/// The [builder] function is called in the following situations: +/// +/// * The first time the widget is laid out. +/// * When the parent widget passes different layout constraints. +/// * When the parent widget updates this widget. +/// * When the dependencies that the [builder] function subscribes to change. +/// +/// The [builder] function is _not_ called during layout if the parent passes +/// the same constraints repeatedly. +/// {@endtemplate} +/// +/// Subclasses must return a [RenderObject] that mixes in +/// [RenderConstrainedLayoutBuilder]. +abstract class ConstrainedLayoutBuilder extends RenderObjectWidget { + /// Creates a widget that defers its building until layout. + const ConstrainedLayoutBuilder({ + super.key, + required this.builder, + }); + + @override + RenderObjectElement createElement() => _LayoutBuilderElement(this); + + /// Called at layout time to construct the widget tree. + /// + /// The builder must not return null. + final Widget Function(BuildContext context, ConstraintType constraints) builder; + +// updateRenderObject is redundant with the logic in the LayoutBuilderElement below. +} + +class _LayoutBuilderElement extends RenderObjectElement { + _LayoutBuilderElement(ConstrainedLayoutBuilder super.widget); + + @override + RenderConstrainedLayoutBuilder get renderObject => super.renderObject as RenderConstrainedLayoutBuilder; + + Element? _child; + + @override + void visitChildren(ElementVisitor visitor) { + if (_child != null) { + visitor(_child!); + } + } + + @override + void forgetChild(Element child) { + assert(child == _child); + _child = null; + super.forgetChild(child); + } + + @override + void mount(Element? parent, Object? newSlot) { + super.mount(parent, newSlot); // Creates the renderObject. + renderObject.updateCallback(_layout); + } + + @override + void update(ConstrainedLayoutBuilder newWidget) { + assert(widget != newWidget); + super.update(newWidget); + assert(widget == newWidget); + + renderObject.updateCallback(_layout); + // Force the callback to be called, even if the layout constraints are the + // same, because the logic in the callback might have changed. + renderObject.markNeedsBuild(); + } + + @override + void performRebuild() { + // This gets called if markNeedsBuild() is called on us. + // That might happen if, e.g., our builder uses Inherited widgets. + + // Force the callback to be called, even if the layout constraints are the + // same. This is because that callback may depend on the updated widget + // configuration, or an inherited widget. + renderObject.markNeedsBuild(); + super.performRebuild(); // Calls widget.updateRenderObject (a no-op in this case). + } + + @override + void unmount() { + renderObject.updateCallback(null); + super.unmount(); + } + + void _layout(ConstraintType constraints) { + @pragma('vm:notify-debugger-on-exception') + void layoutCallback() { + Widget built; + try { + built = (widget as ConstrainedLayoutBuilder).builder(this, constraints); + debugWidgetBuilderValue(widget, built); + } catch (e, stack) { + built = ErrorWidget.builder( + _reportException( + ErrorDescription('building $widget'), + e, + stack, + informationCollector: () => [ + if (kDebugMode) + DiagnosticsDebugCreator(DebugCreator(this)), + ], + ), + ); + } + try { + _child = updateChild(_child, built, null); + assert(_child != null); + } catch (e, stack) { + built = ErrorWidget.builder( + _reportException( + ErrorDescription('building $widget'), + e, + stack, + informationCollector: () => [ + if (kDebugMode) + DiagnosticsDebugCreator(DebugCreator(this)), + ], + ), + ); + _child = updateChild(null, built, slot); + } + } + + owner!.buildScope(this, layoutCallback); + } + + @override + void insertRenderObjectChild(RenderObject child, Object? slot) { + final RenderObjectWithChildMixin renderObject = this.renderObject; + assert(slot == null); + assert(renderObject.debugValidateChild(child)); + renderObject.child = child; + assert(renderObject == this.renderObject); + } + + @override + void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) { + assert(false); + } + + @override + void removeRenderObjectChild(RenderObject child, Object? slot) { + final RenderConstrainedLayoutBuilder renderObject = this.renderObject; + assert(renderObject.child == child); + renderObject.child = null; + assert(renderObject == this.renderObject); + } +} + +/// Generic mixin for [RenderObject]s created by [ConstrainedLayoutBuilder]. +/// +/// Provides a callback that should be called at layout time, typically in +/// [RenderObject.performLayout]. +mixin RenderConstrainedLayoutBuilder on RenderObjectWithChildMixin { + LayoutCallback? _callback; + /// Change the layout callback. + void updateCallback(LayoutCallback? value) { + if (value == _callback) { + return; + } + _callback = value; + markNeedsLayout(); + } + + bool _needsBuild = true; + + /// Marks this layout builder as needing to rebuild. + /// + /// The layout build rebuilds automatically when layout constraints change. + /// However, we must also rebuild when the widget updates, e.g. after + /// [State.setState], or [State.didChangeDependencies], even when the layout + /// constraints remain unchanged. + /// + /// See also: + /// + /// * [ConstrainedLayoutBuilder.builder], which is called during the rebuild. + void markNeedsBuild() { + // Do not call the callback directly. It must be called during the layout + // phase, when parent constraints are available. Calling `markNeedsLayout` + // will cause it to be called at the right time. + _needsBuild = true; + markNeedsLayout(); + } + + // The constraints that were passed to this class last time it was laid out. + // These constraints are compared to the new constraints to determine whether + // [ConstrainedLayoutBuilder.builder] needs to be called. + Constraints? _previousConstraints; + + /// Invoke the callback supplied via [updateCallback]. + /// + /// Typically this results in [ConstrainedLayoutBuilder.builder] being called + /// during layout. + void rebuildIfNecessary() { + assert(_callback != null); + if (_needsBuild || constraints != _previousConstraints) { + _previousConstraints = constraints; + _needsBuild = false; + invokeLayoutCallback(_callback!); + } + } +} + +/// Builds a widget tree that can depend on the parent widget's size. +/// +/// Similar to the [Builder] widget except that the framework calls the [builder] +/// function at layout time and provides the parent widget's constraints. This +/// is useful when the parent constrains the child's size and doesn't depend on +/// the child's intrinsic size. The [LayoutBuilder]'s final size will match its +/// child's size. +/// +/// {@macro flutter.widgets.ConstrainedLayoutBuilder} +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=IYDVcriKjsw} +/// +/// If the child should be smaller than the parent, consider wrapping the child +/// in an [Align] widget. If the child might want to be bigger, consider +/// wrapping it in a [SingleChildScrollView] or [OverflowBox]. +/// +/// {@tool dartpad} +/// This example uses a [LayoutBuilder] to build a different widget depending on the available width. Resize the +/// DartPad window to see [LayoutBuilder] in action! +/// +/// ** See code in examples/api/lib/widgets/layout_builder/layout_builder.0.dart ** +/// {@end-tool} +/// +/// See also: +/// +/// * [SliverLayoutBuilder], the sliver counterpart of this widget. +/// * [Builder], which calls a `builder` function at build time. +/// * [StatefulBuilder], which passes its `builder` function a `setState` callback. +/// * [CustomSingleChildLayout], which positions its child during layout. +/// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/). +class LayoutBuilder extends ConstrainedLayoutBuilder { + /// Creates a widget that defers its building until layout. + const LayoutBuilder({ + super.key, + required super.builder, + }); + + @override + RenderObject createRenderObject(BuildContext context) => _RenderLayoutBuilder(); +} + +class _RenderLayoutBuilder extends RenderBox with RenderObjectWithChildMixin, RenderConstrainedLayoutBuilder { + @override + double computeMinIntrinsicWidth(double height) { + assert(_debugThrowIfNotCheckingIntrinsics()); + return 0.0; + } + + @override + double computeMaxIntrinsicWidth(double height) { + assert(_debugThrowIfNotCheckingIntrinsics()); + return 0.0; + } + + @override + double computeMinIntrinsicHeight(double width) { + assert(_debugThrowIfNotCheckingIntrinsics()); + return 0.0; + } + + @override + double computeMaxIntrinsicHeight(double width) { + assert(_debugThrowIfNotCheckingIntrinsics()); + return 0.0; + } + + @override + Size computeDryLayout(BoxConstraints constraints) { + assert(debugCannotComputeDryLayout(reason: + 'Calculating the dry layout would require running the layout callback ' + 'speculatively, which might mutate the live render object tree.', + )); + return Size.zero; + } + + @override + void performLayout() { + final BoxConstraints constraints = this.constraints; + rebuildIfNecessary(); + if (child != null) { + child!.layout(constraints, parentUsesSize: true); + size = constraints.constrain(child!.size); + } else { + size = constraints.biggest; + } + } + + @override + double? computeDistanceToActualBaseline(TextBaseline baseline) { + if (child != null) { + return child!.getDistanceToActualBaseline(baseline); + } + return super.computeDistanceToActualBaseline(baseline); + } + + @override + bool hitTestChildren(BoxHitTestResult result, { required Offset position }) { + return child?.hitTest(result, position: position) ?? false; + } + + @override + void paint(PaintingContext context, Offset offset) { + if (child != null) { + context.paintChild(child!, offset); + } + } + + bool _debugThrowIfNotCheckingIntrinsics() { + assert(() { + if (!RenderObject.debugCheckingIntrinsics) { + throw FlutterError( + 'LayoutBuilder does not support returning intrinsic dimensions.\n' + 'Calculating the intrinsic dimensions would require running the layout ' + 'callback speculatively, which might mutate the live render object tree.', + ); + } + return true; + }()); + + return true; + } +} + +FlutterErrorDetails _reportException( + DiagnosticsNode context, + Object exception, + StackTrace stack, { + InformationCollector? informationCollector, + }) { + final FlutterErrorDetails details = FlutterErrorDetails( + exception: exception, + stack: stack, + library: 'widgets library', + context: context, + informationCollector: informationCollector, + ); + FlutterError.reportError(details); + return details; +} diff --git a/tdesign-component/lib/src/components/indexes/sticky_header/sticky_header_render.dart b/tdesign-component/lib/src/components/indexes/sticky_header/sticky_header_render.dart new file mode 100644 index 000000000..3ed4bdb9a --- /dev/null +++ b/tdesign-component/lib/src/components/indexes/sticky_header/sticky_header_render.dart @@ -0,0 +1,396 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:tdesign_flutter_adaptation/components/indexes/sticky_header/value_layout_builder.dart'; + +import 'sticky_header_widget.dart'; + +/// A sliver with a [RenderBox] as header and a [RenderSliver] as child. +/// +/// The [header] stays pinned when it hits the start of the viewport until +/// the [child] scrolls off the viewport. +class RenderSliverStickyHeader extends RenderSliver with RenderSliverHelpers { + RenderSliverStickyHeader({ + RenderObject? header, + RenderSliver? child, + bool overlapsContent = false, + bool sticky = true, + double pinnedOffset = 0.0, + StickyHeaderController? controller, + }) : _overlapsContent = overlapsContent, + _sticky = sticky, + _pinnedOffset = pinnedOffset, + _controller = controller { + this.header = header as RenderBox?; + this.child = child; + } + + SliverStickyHeaderState? _oldState; + double? _headerExtent; + late bool _isPinned; + + bool get overlapsContent => _overlapsContent; + bool _overlapsContent; + + set overlapsContent(bool value) { + if (_overlapsContent == value) { + return; + } + _overlapsContent = value; + markNeedsLayout(); + } + + bool get sticky => _sticky; + bool _sticky; + + set sticky(bool value) { + if (_sticky == value) { + return; + } + _sticky = value; + markNeedsLayout(); + } + + double get pinnedOffset => _pinnedOffset; + double _pinnedOffset; + + set pinnedOffset(double value) { + if (_pinnedOffset == value) { + return; + } + _pinnedOffset = value; + markNeedsLayout(); + } + + StickyHeaderController? get controller => _controller; + StickyHeaderController? _controller; + + set controller(StickyHeaderController? value) { + if (_controller == value) { + return; + } + if (_controller != null && value != null) { + // We copy the state of the old controller. + value.stickyHeaderScrollOffset = _controller!.stickyHeaderScrollOffset; + } + _controller = value; + } + + /// The render object's header + RenderBox? get header => _header; + RenderBox? _header; + + set header(RenderBox? value) { + if (_header != null) { + dropChild(_header!); + } + _header = value; + if (_header != null) { + adoptChild(_header!); + } + } + + /// The render object's unique child + RenderSliver? get child => _child; + RenderSliver? _child; + + set child(RenderSliver? value) { + if (_child != null) { + dropChild(_child!); + } + _child = value; + if (_child != null) { + adoptChild(_child!); + } + } + + @override + void setupParentData(RenderObject child) { + if (child.parentData is! SliverPhysicalParentData) { + child.parentData = SliverPhysicalParentData(); + } + } + + @override + void attach(PipelineOwner owner) { + super.attach(owner); + if (_header != null) { + _header!.attach(owner); + } + if (_child != null) { + _child!.attach(owner); + } + } + + @override + void detach() { + super.detach(); + if (_header != null) { + _header!.detach(); + } + if (_child != null) { + _child!.detach(); + } + } + + @override + void redepthChildren() { + if (_header != null) { + redepthChild(_header!); + } + if (_child != null) { + redepthChild(_child!); + } + } + + @override + void visitChildren(RenderObjectVisitor visitor) { + if (_header != null) { + visitor(_header!); + } + if (_child != null) { + visitor(_child!); + } + } + + @override + List debugDescribeChildren() { + final result = []; + if (header != null) { + result.add(header!.toDiagnosticsNode(name: 'header')); + } + if (child != null) { + result.add(child!.toDiagnosticsNode(name: 'child')); + } + return result; + } + + double computeHeaderExtent() { + if (header == null) { + return 0.0; + } + assert(header!.hasSize); + switch (constraints.axis) { + case Axis.vertical: + return header!.size.height; + case Axis.horizontal: + return header!.size.width; + } + } + + double? get headerLogicalExtent => overlapsContent ? 0.0 : _headerExtent; + + @override + void performLayout() { + if (header == null && child == null) { + geometry = SliverGeometry.zero; + return; + } + + // One of them is not null. + final axisDirection = applyGrowthDirectionToAxisDirection(constraints.axisDirection, constraints.growthDirection); + + if (header != null) { + header!.layout( + BoxValueConstraints( + value: _oldState ?? const SliverStickyHeaderState(0.0, false), + constraints: constraints.asBoxConstraints(), + ), + parentUsesSize: true, + ); + _headerExtent = computeHeaderExtent(); + } + + // Compute the header extent only one time. + final headerExtent = headerLogicalExtent!; + final headerPaintExtent = calculatePaintOffset(constraints, from: 0.0, to: headerExtent); + final headerCacheExtent = calculateCacheOffset(constraints, from: 0.0, to: headerExtent); + + if (child == null) { + geometry = SliverGeometry( + scrollExtent: headerExtent, + maxPaintExtent: headerExtent, + paintExtent: headerPaintExtent, + cacheExtent: headerCacheExtent, + hitTestExtent: headerPaintExtent, + hasVisualOverflow: headerExtent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0); + } else { + child!.layout( + constraints.copyWith( + scrollOffset: math.max(0.0, constraints.scrollOffset - headerExtent), + cacheOrigin: math.min(0.0, constraints.cacheOrigin + headerExtent), + overlap: math.min(headerExtent, constraints.scrollOffset) + (sticky ? constraints.overlap : 0), + remainingPaintExtent: constraints.remainingPaintExtent - headerPaintExtent, + remainingCacheExtent: constraints.remainingCacheExtent - headerCacheExtent, + ), + parentUsesSize: true, + ); + final childLayoutGeometry = child!.geometry!; + if (childLayoutGeometry.scrollOffsetCorrection != null) { + geometry = SliverGeometry( + scrollOffsetCorrection: childLayoutGeometry.scrollOffsetCorrection, + ); + return; + } + + final double paintExtent = math.min( + headerPaintExtent + math.max(childLayoutGeometry.paintExtent, childLayoutGeometry.layoutExtent), + constraints.remainingPaintExtent, + ); + + geometry = SliverGeometry( + scrollExtent: headerExtent + childLayoutGeometry.scrollExtent, + maxScrollObstructionExtent: sticky ? headerPaintExtent : 0, + paintExtent: paintExtent, + layoutExtent: math.min(headerPaintExtent + childLayoutGeometry.layoutExtent, paintExtent), + cacheExtent: math.min(headerCacheExtent + childLayoutGeometry.cacheExtent, constraints.remainingCacheExtent), + maxPaintExtent: headerExtent + childLayoutGeometry.maxPaintExtent, + hitTestExtent: math.max( + headerPaintExtent + childLayoutGeometry.paintExtent, headerPaintExtent + childLayoutGeometry.hitTestExtent), + hasVisualOverflow: childLayoutGeometry.hasVisualOverflow, + ); + + final childParentData = child!.parentData as SliverPhysicalParentData?; + switch (axisDirection) { + case AxisDirection.up: + childParentData!.paintOffset = Offset.zero; + break; + case AxisDirection.right: + childParentData!.paintOffset = Offset(calculatePaintOffset(constraints, from: 0.0, to: headerExtent), 0.0); + break; + case AxisDirection.down: + childParentData!.paintOffset = Offset(0.0, calculatePaintOffset(constraints, from: 0.0, to: headerExtent)); + break; + case AxisDirection.left: + childParentData!.paintOffset = Offset.zero; + break; + } + } + + if (header != null) { + final headerParentData = header!.parentData as SliverPhysicalParentData?; + final childScrollExtent = child?.geometry?.scrollExtent ?? 0.0; + final headerPosition = sticky + ? math.min(constraints.overlap, + childScrollExtent - constraints.scrollOffset - (overlapsContent ? _headerExtent! : 0.0)) + : -constraints.scrollOffset; + + _isPinned = sticky && + ((constraints.scrollOffset + constraints.overlap) > 0.0 || + constraints.remainingPaintExtent == constraints.viewportMainAxisExtent); + + final headerScrollRatio = ((headerPosition - constraints.overlap).abs() / _headerExtent!); + if (_isPinned && headerScrollRatio <= 1) { + controller?.stickyHeaderScrollOffset = constraints.precedingScrollExtent; + } + // second layout if scroll percentage changed and header is a + // RenderStickyHeaderLayoutBuilder. + if (header is RenderConstrainedLayoutBuilder, RenderBox>) { + final headerScrollRatioClamped = headerScrollRatio.clamp(0.0, 1.0); + + final state = SliverStickyHeaderState(headerScrollRatioClamped, _isPinned); + if (_oldState != state) { + _oldState = state; + header!.layout( + BoxValueConstraints( + value: _oldState!, + constraints: constraints.asBoxConstraints(), + ), + parentUsesSize: true, + ); + } + } + + switch (axisDirection) { + case AxisDirection.up: + headerParentData!.paintOffset = Offset(0.0, geometry!.paintExtent - headerPosition - _headerExtent! - pinnedOffset); + break; + case AxisDirection.down: + headerParentData!.paintOffset = Offset(0.0, headerPosition + pinnedOffset); + break; + case AxisDirection.left: + headerParentData!.paintOffset = Offset(geometry!.paintExtent - headerPosition - _headerExtent! - pinnedOffset, 0.0); + break; + case AxisDirection.right: + headerParentData!.paintOffset = Offset(headerPosition + pinnedOffset, 0.0); + break; + } + } + } + + @override + bool hitTestChildren(SliverHitTestResult result, + {required double mainAxisPosition, required double crossAxisPosition}) { + assert(geometry!.hitTestExtent > 0.0); + final childScrollExtent = child?.geometry?.scrollExtent ?? 0.0; + final headerPosition = sticky + ? math.min(constraints.overlap, + childScrollExtent - constraints.scrollOffset - (overlapsContent ? _headerExtent! : 0.0)) + : -constraints.scrollOffset; + + if (header != null && (mainAxisPosition - headerPosition) <= _headerExtent!) { + final didHitHeader = hitTestBoxChild( + BoxHitTestResult.wrap(SliverHitTestResult.wrap(result)), + header!, + mainAxisPosition: mainAxisPosition - childMainAxisPosition(header) - headerPosition, + crossAxisPosition: crossAxisPosition, + ); + + return didHitHeader || + (_overlapsContent && + child != null && + child!.geometry!.hitTestExtent > 0.0 && + child!.hitTest(result, + mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), + crossAxisPosition: crossAxisPosition)); + } else if (child != null && child!.geometry!.hitTestExtent > 0.0) { + return child!.hitTest(result, + mainAxisPosition: mainAxisPosition - childMainAxisPosition(child), crossAxisPosition: crossAxisPosition); + } + return false; + } + + @override + double childMainAxisPosition(RenderObject? child) { + if (child == header) { + return _isPinned ? 0.0 : -(constraints.scrollOffset + constraints.overlap); + } + if (child == this.child) { + return calculatePaintOffset(constraints, from: 0.0, to: headerLogicalExtent!); + } + return 0; + } + + @override + double? childScrollOffset(RenderObject child) { + assert(child.parent == this); + if (child == this.child) { + return headerLogicalExtent; + } else { + return super.childScrollOffset(child); + } + } + + @override + void applyPaintTransform(RenderObject child, Matrix4 transform) { + final childParentData = child.parentData as SliverPhysicalParentData; + childParentData.applyPaintTransform(transform); + } + + @override + void paint(PaintingContext context, Offset offset) { + if (geometry!.visible) { + if (child != null && child!.geometry!.visible) { + final childParentData = child!.parentData as SliverPhysicalParentData; + context.paintChild(child!, offset + childParentData.paintOffset); + } + + // The header must be drawn over the sliver. + if (header != null) { + final headerParentData = header!.parentData as SliverPhysicalParentData; + context.paintChild(header!, offset + headerParentData.paintOffset); + } + } + } +} diff --git a/tdesign-component/lib/src/components/indexes/sticky_header/sticky_header_widget.dart b/tdesign-component/lib/src/components/indexes/sticky_header/sticky_header_widget.dart new file mode 100644 index 000000000..7ea111b78 --- /dev/null +++ b/tdesign-component/lib/src/components/indexes/sticky_header/sticky_header_widget.dart @@ -0,0 +1,314 @@ +import 'package:flutter/rendering.dart'; +import 'package:flutter/widgets.dart'; +import 'package:tdesign_flutter_adaptation/components/indexes/sticky_header/value_layout_builder.dart'; + +import 'sticky_header_render.dart'; + +/// Signature used by [SliverStickyHeader.builder] to build the header +/// when the sticky header state has changed. +typedef SliverStickyHeaderWidgetBuilder = Widget Function( + BuildContext context, + SliverStickyHeaderState state, +); + +// ignore: prefer_mixin +class StickyHeaderController with ChangeNotifier { + /// The offset to use in order to jump to the first item + /// of current the sticky header. + /// + /// If there is no sticky headers, this is 0. + double get stickyHeaderScrollOffset => _stickyHeaderScrollOffset; + double _stickyHeaderScrollOffset = 0; + + /// This setter should only be used by flutter_sticky_header package. + set stickyHeaderScrollOffset(double value) { + if (_stickyHeaderScrollOffset != value) { + _stickyHeaderScrollOffset = value; + notifyListeners(); + } + } +} + +/// The [StickyHeaderController] for descendant widgets that don't specify one +/// explicitly. +/// +/// [DefaultStickyHeaderController] is an inherited widget that is used to share a +/// [StickyHeaderController] with [SliverStickyHeader]s. It's used when sharing an +/// explicitly created [StickyHeaderController] isn't convenient because the sticky +/// headers are created by a stateless parent widget or by different parent +/// widgets. +class DefaultStickyHeaderController extends StatefulWidget { + const DefaultStickyHeaderController({ + Key? key, + required this.child, + }) : super(key: key); + + /// The widget below this widget in the tree. + /// + /// Typically a [Scaffold] whose [AppBar] includes a [TabBar]. + /// + /// {@macro flutter.widgets.child} + final Widget child; + + /// The closest instance of this class that encloses the given context. + /// + /// Typical usage: + /// + /// ```dart + /// StickyHeaderController controller = DefaultStickyHeaderController.of(context); + /// ``` + static StickyHeaderController? of(BuildContext context) { + final scope = context.dependOnInheritedWidgetOfExactType<_StickyHeaderControllerScope>(); + return scope?.controller; + } + + @override + _DefaultStickyHeaderControllerState createState() => _DefaultStickyHeaderControllerState(); +} + +class _DefaultStickyHeaderControllerState extends State { + StickyHeaderController? _controller; + + @override + void initState() { + super.initState(); + _controller = StickyHeaderController(); + } + + @override + void dispose() { + _controller!.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return _StickyHeaderControllerScope( + controller: _controller, + child: widget.child, + ); + } +} + +class _StickyHeaderControllerScope extends InheritedWidget { + const _StickyHeaderControllerScope({ + Key? key, + this.controller, + required Widget child, + }) : super(key: key, child: child); + + final StickyHeaderController? controller; + + @override + bool updateShouldNotify(_StickyHeaderControllerScope old) { + return controller != old.controller; + } +} + +/// State describing how a sticky header is rendered. +@immutable +class SliverStickyHeaderState { + const SliverStickyHeaderState( + this.scrollPercentage, + this.isPinned, + ); + + final double scrollPercentage; + + final bool isPinned; + + @override + bool operator ==(dynamic other) { + if (identical(this, other)) { + return true; + } + if (other is! SliverStickyHeaderState) { + return false; + } + final typedOther = other; + return scrollPercentage == typedOther.scrollPercentage && isPinned == typedOther.isPinned; + } + + @override + int get hashCode { + return Object.hash(scrollPercentage, isPinned); + } +} + +/// A sliver that displays a header before its sliver. +/// The header scrolls off the viewport only when the sliver does. +/// +/// Place this widget inside a [CustomScrollView] or similar. +class SliverStickyHeader extends RenderObjectWidget { + /// Creates a sliver that displays the [header] before its [sliver], unless + /// [overlapsContent] it's true. + /// The [header] stays pinned when it hits the start of the viewport until + /// the [sliver] scrolls off the viewport. + /// + /// The [overlapsContent] and [sticky] arguments must not be null. + /// + /// If a [StickyHeaderController] is not provided, then the value of + /// [DefaultStickyHeaderController.of] will be used. + const SliverStickyHeader({ + Key? key, + this.header, + this.sliver, + this.overlapsContent = false, + this.sticky = true, + this.pinnedOffset = 0.0, + this.controller, + }) : super(key: key); + + /// Creates a widget that builds the header of a [SliverStickyHeader] + /// each time its scroll percentage changes. + /// + /// The [builder], [overlapsContent] and [sticky] arguments must not be null. + /// + /// If a [StickyHeaderController] is not provided, then the value of + /// [DefaultStickyHeaderController.of] will be used. + SliverStickyHeader.builder({ + Key? key, + required SliverStickyHeaderWidgetBuilder builder, + Widget? sliver, + bool overlapsContent = false, + bool sticky = true, + double pinnedOffset = 0.0, + StickyHeaderController? controller, + }) : this( + key: key, + header: ValueLayoutBuilder( + builder: (context, constraints) => builder(context, constraints.value), + ), + sliver: sliver, + overlapsContent: overlapsContent, + sticky: sticky, + pinnedOffset: pinnedOffset, + controller: controller, + ); + + /// The header to display before the sliver. + final Widget? header; + + /// The sliver to display after the header. + final Widget? sliver; + + /// Whether the header should be drawn on top of the sliver + /// instead of before. + final bool overlapsContent; + + /// Whether to stick the header. + /// Defaults to true. + final bool sticky; + + /// The offset at which to pin the header. + /// Defaults to 0.0. + final double pinnedOffset; + + /// The controller used to interact with this sliver. + /// + /// If a [StickyHeaderController] is not provided, then the value of [DefaultStickyHeaderController.of] + /// will be used. + final StickyHeaderController? controller; + + @override + RenderSliverStickyHeader createRenderObject(BuildContext context) { + return RenderSliverStickyHeader( + overlapsContent: overlapsContent, + sticky: sticky, + pinnedOffset: sticky ? pinnedOffset : 0.0, + controller: controller ?? DefaultStickyHeaderController.of(context), + ); + } + + @override + SliverStickyHeaderRenderObjectElement createElement() => SliverStickyHeaderRenderObjectElement(this); + + @override + void updateRenderObject( + BuildContext context, + RenderSliverStickyHeader renderObject, + ) { + renderObject + ..overlapsContent = overlapsContent + ..sticky = sticky + ..pinnedOffset = sticky ? pinnedOffset : 0.0 + ..controller = controller ?? DefaultStickyHeaderController.of(context); + } +} + +class SliverStickyHeaderRenderObjectElement extends RenderObjectElement { + /// Creates an element that uses the given widget as its configuration. + SliverStickyHeaderRenderObjectElement(SliverStickyHeader widget) : super(widget); + + @override + SliverStickyHeader get widget => super.widget as SliverStickyHeader; + + Element? _header; + + Element? _sliver; + + @override + void visitChildren(ElementVisitor visitor) { + if (_header != null) { + visitor(_header!); + } + if (_sliver != null) { + visitor(_sliver!); + } + } + + @override + void forgetChild(Element child) { + super.forgetChild(child); + if (child == _header) { + _header = null; + } + if (child == _sliver) { + _sliver = null; + } + } + + @override + void mount(Element? parent, dynamic newSlot) { + super.mount(parent, newSlot); + _header = updateChild(_header, widget.header, 0); + _sliver = updateChild(_sliver, widget.sliver, 1); + } + + @override + void update(SliverStickyHeader newWidget) { + super.update(newWidget); + assert(widget == newWidget); + _header = updateChild(_header, widget.header, 0); + _sliver = updateChild(_sliver, widget.sliver, 1); + } + + @override + void insertRenderObjectChild(RenderObject child, int? slot) { + final renderObject = this.renderObject as RenderSliverStickyHeader; + if (slot == 0) { + renderObject.header = child as RenderBox?; + } + if (slot == 1) { + renderObject.child = child as RenderSliver?; + } + assert(renderObject == this.renderObject); + } + + @override + void moveRenderObjectChild(RenderObject child, slot, newSlot) { + assert(false); + } + + @override + void removeRenderObjectChild(RenderObject child, slot) { + final renderObject = this.renderObject as RenderSliverStickyHeader; + if (renderObject.header == child) { + renderObject.header = null; + } + if (renderObject.child == child) { + renderObject.child = null; + } + assert(renderObject == this.renderObject); + } +} diff --git a/tdesign-component/lib/src/components/indexes/td_indexes.dart b/tdesign-component/lib/src/components/indexes/td_indexes.dart new file mode 100644 index 000000000..bd5b32e25 --- /dev/null +++ b/tdesign-component/lib/src/components/indexes/td_indexes.dart @@ -0,0 +1,216 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/iterable_ext.dart'; + +export 'sticky_header/sticky_header_widget.dart'; +export 'td_indexes_anchor.dart'; +export 'td_indexes_list.dart'; + +/// 索引 +class TDIndexes extends StatefulWidget { + const TDIndexes({ + Key? key, + this.indexList, + this.indexListMaxHeight = 0.8, + this.sticky = true, + this.stickyOffset = 0, + this.capsuleTheme = false, + this.reverse = false, + this.scrollController, + this.onChange, + this.onSelect, + required this.builderContent, + this.builderAnchor, + this.builderIndex, + }) : super(key: key); + + /// 索引字符列表。不传默认 A-Z + final List? indexList; + + /// 索引列表最大高度(父容器高度的百分比,默认0.8) + final double? indexListMaxHeight; + + /// 锚点是否吸顶 + final bool? sticky; + + /// 锚点吸顶时与顶部的距离 + final double? stickyOffset; + + /// 锚点是否为胶囊式样式 + final bool? capsuleTheme; + + /// 反方向滚动置顶 + final bool? reverse; + + /// 滚动控制器 + final ScrollController? scrollController; + + /// 索引发生变更时触发事件 + final void Function(String index)? onChange; + + /// 点击侧边栏时触发事件 + final void Function(String index)? onSelect; + + /// 内容自定义构建 + final Widget? Function(BuildContext context, String index) builderContent; + + /// 锚点自定义构建 + final Widget? Function(BuildContext context, String index, bool isPinnedToTop)? builderAnchor; + + /// 索引文本自定义构建,包括索引激活左侧提示 + final Widget Function(BuildContext context, String index, bool isActive)? builderIndex; + + @override + _TDIndexesState createState() => _TDIndexesState(); +} + +class _TDIndexesState extends State { + late List _indexList; + late ValueNotifier _activeIndex; + late ScrollController _scrollController; + final _anchorKeys = {}; + final _contentKeys = {}; + var _isAnimating = false; + + @override + void initState() { + super.initState(); + _indexList = widget.indexList ?? _azList(); + _activeIndex = ValueNotifier(_indexList.getOrNull(0) ?? ''); + _scrollController = widget.scrollController ?? ScrollController(); + } + + @override + void didUpdateWidget(TDIndexes oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.indexList != oldWidget.indexList) { + _indexList = widget.indexList ?? _azList(); + _activeIndex = ValueNotifier(_indexList.getOrNull(0) ?? ''); + } + if (widget.scrollController != oldWidget.scrollController) { + _scrollController.dispose(); + _scrollController = widget.scrollController ?? ScrollController(); + } + } + + @override + void dispose() { + _scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + color: TDTheme.of(context).whiteColor1, + child: Stack( + children: [ + CustomScrollView( + controller: _scrollController, + reverse: widget.reverse ?? false, + slivers: _slivers(), + ), + TDIndexesList( + indexList: _indexList, + activeIndex: _activeIndex, + onSelect: (newIndex, oldIndex) { + widget.onSelect?.call(newIndex); + widget.onChange?.call(newIndex); + _scrollToTarget(newIndex, oldIndex); + }, + indexListMaxHeight: widget.indexListMaxHeight ?? 0.8, + builderIndex: widget.builderIndex, + ), + ], + ), + ); + } + + List _slivers() { + final capsuleTheme = widget.capsuleTheme ?? false; + final stickyOffset = widget.stickyOffset ?? 0; + _anchorKeys.clear(); + _contentKeys.clear(); + return _indexList.map((e) { + final isPinnedOffset = capsuleTheme && _activeIndex.value == e; + return SliverStickyHeader.builder( + sticky: widget.sticky ?? true, + pinnedOffset: isPinnedOffset ? TDTheme.of(context).spacer8 + stickyOffset : stickyOffset, + builder: (context, state) { + _anchorKeys[e] = context; + if (state.isPinned && _activeIndex.value != e && !_isAnimating) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _activeIndex.value = e; + widget.onChange?.call(e); + }); + } + return TDIndexesAnchor( + text: e, + capsuleTheme: capsuleTheme, + activeIndex: _activeIndex, + builderAnchor: widget.builderAnchor, + sticky: widget.sticky ?? true, + ); + }, + sliver: SliverToBoxAdapter( + child: Builder( + builder: (context) { + _contentKeys[e] = context; + return Padding( + padding: isPinnedOffset ? EdgeInsets.only(top: TDTheme.of(context).spacer8) : EdgeInsets.zero, + child: widget.builderContent(context, e), + ); + }, + ), + ), + ); + }).toList(); + } + + List _azList() { + final azList = []; + for (var i = 65; i <= 90; i++) { + azList.add(String.fromCharCode(i)); + } + return azList; + } + + void _scrollToTarget(String newIndex, String oldIndex) { + _isAnimating = true; + + /// isUp: 是否(手指)向上滑动 + final isUp = _indexList.indexOf(newIndex) > _indexList.indexOf(oldIndex); + if (isUp) { + var index = oldIndex; + final contentRenderBox = _contentKeys[index]?.findRenderObject() as RenderBox?; + if (contentRenderBox != null) { + final contentHeight = contentRenderBox.size.height; + final maxScrollExtent = _scrollController.position.maxScrollExtent; + final targetOffset = + contentRenderBox.localToGlobal(Offset(0, contentHeight), ancestor: context.findRenderObject()); + final scrollOffset = targetOffset.dy + _scrollController.offset; + _scrollController.jumpTo(min(maxScrollExtent, scrollOffset)); + } + index = _indexList[_indexList.indexOf(index) + 1]; + WidgetsBinding.instance.addPostFrameCallback((_) { + if (index != newIndex) { + _scrollToTarget(newIndex, index); + } else { + _isAnimating = false; + } + }); + } else { + final anchorContext = _anchorKeys[newIndex]; + if (anchorContext != null) { + Scrollable.ensureVisible(anchorContext).then((value) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _isAnimating = false; + }); + }); + } + } + } +} diff --git a/tdesign-component/lib/src/components/indexes/td_indexes_anchor.dart b/tdesign-component/lib/src/components/indexes/td_indexes_anchor.dart new file mode 100644 index 000000000..8e80163c7 --- /dev/null +++ b/tdesign-component/lib/src/components/indexes/td_indexes_anchor.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +/// 索引锚点 +class TDIndexesAnchor extends StatelessWidget { + const TDIndexesAnchor({ + Key? key, + required this.sticky, + required this.text, + required this.capsuleTheme, + this.builderAnchor, + required this.activeIndex, + }) : super(key: key); + + /// 索引是否吸顶 + final bool sticky; + + /// 锚点文本 + final String text; + + /// 是否为胶囊式样式 + final bool capsuleTheme; + + /// 选中索引 + final ValueNotifier activeIndex; + + /// 索引锚点构建 + final Widget? Function(BuildContext context, String index, bool isPinnedToTop)? builderAnchor; + + @override + Widget build(BuildContext context) { + return ValueListenableBuilder( + valueListenable: activeIndex, + builder: (context, value, child) { + final isPinned = value == text; + final customAnchor = builderAnchor?.call(context, text, isPinned); + return customAnchor ?? + Container( + padding: + EdgeInsets.symmetric(vertical: TDTheme.of(context).spacer4, horizontal: TDTheme.of(context).spacer16), + margin: capsuleTheme ? EdgeInsets.symmetric(horizontal: TDTheme.of(context).spacer8) : null, + decoration: BoxDecoration( + color: isPinned ? TDTheme.of(context).whiteColor1 : TDTheme.of(context).grayColor1, + borderRadius: capsuleTheme ? BorderRadius.circular(TDTheme.of(context).radiusCircle) : null, + border: isPinned + ? capsuleTheme + ? Border.all(color: TDTheme.of(context).grayColor1) + : Border(bottom: BorderSide(color: TDTheme.of(context).grayColor1)) + : null, + ), + child: TDText( + text, + forceVerticalCenter: true, + font: isPinned ? TDTheme.of(context).fontMarkMedium : TDTheme.of(context).fontTitleSmall, + textColor: isPinned ? TDTheme.of(context).brandColor7 : TDTheme.of(context).fontGyColor1, + ), + ); + }, + ); + } +} diff --git a/tdesign-component/lib/src/components/indexes/td_indexes_list.dart b/tdesign-component/lib/src/components/indexes/td_indexes_list.dart new file mode 100644 index 000000000..1bff3360b --- /dev/null +++ b/tdesign-component/lib/src/components/indexes/td_indexes_list.dart @@ -0,0 +1,198 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/scheduler.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/iterable_ext.dart'; +import 'sticky_header/sticky_header_widget.dart'; +import 'td_indexes_anchor.dart'; + +/// 索引 +class TDIndexesList extends StatefulWidget { + const TDIndexesList({ + Key? key, + required this.indexList, + this.indexListMaxHeight = 0.8, + required this.activeIndex, + required this.onSelect, + this.builderIndex, + }) : super(key: key); + + /// 索引字符列表。不传默认 A-Z + final List indexList; + + /// 索引列表最大高度(父容器高度的百分比,默认0.8) + final double indexListMaxHeight; + + /// 选中索引 + final ValueNotifier activeIndex; + + /// 点击侧边栏时触发事件 + final void Function(String newIndex, String oldIndex) onSelect; + + /// 索引文本自定义构建,包括索引激活左侧提示 + final Widget Function(BuildContext context, String index, bool isActive)? builderIndex; + + @override + State createState() => _TDIndexesListState(); +} + +class _TDIndexesListState extends State { + late Map _containerKeys; + final _indexSize = 20.0; + Timer? _hideTipTimer; + var _showTip = false; + + @override + void initState() { + super.initState(); + _containerKeys = widget.indexList.asMap().map((index, e) => MapEntry(e, GlobalKey())); + } + + @override + void didUpdateWidget(TDIndexesList oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.indexList != oldWidget.indexList) { + _containerKeys = widget.indexList.asMap().map((index, e) => MapEntry(e, GlobalKey())); + } + } + + @override + void dispose() { + _hideTipTimer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Positioned( + right: TDTheme.of(context).spacer8, + top: 0, + bottom: 0, + child: Align( + child: FractionallySizedBox( + heightFactor: widget.indexListMaxHeight, + child: Align( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onVerticalDragUpdate: (details) { + _changeSelect(details.globalPosition); + }, + onTapUp: (details) { + _changeSelect(details.globalPosition); + _hideTip(); + }, + onVerticalDragEnd: (details) { + _hideTip(); + }, + child: ValueListenableBuilder( + valueListenable: widget.activeIndex, + builder: (context, value, child) { + return Column( + mainAxisSize: MainAxisSize.min, + children: widget.indexList.map( + (e) { + final isActive = value == e; + if (widget.builderIndex != null) { + return widget.builderIndex!(context, e, isActive); + } + return Stack( + clipBehavior: Clip.none, + children: [ + if (_showTip && value == e) + Positioned( + top: -TDTheme.of(context).spacer48 / 2 + _indexSize / 2, + left: -TDTheme.of(context).spacer48, + child: Container( + height: TDTheme.of(context).spacer48, + width: TDTheme.of(context).spacer48, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusCircle), + color: TDTheme.of(context).brandColor1, + ), + child: Center( + child: TDText( + e, + forceVerticalCenter: true, + font: TDTheme.of(context).fontTitleExtraLarge, + textColor: TDTheme.of(context).brandColor7, + ), + ), + ), + ), + Container( + key: _containerKeys[e], + padding: EdgeInsets.only(left: TDTheme.of(context).spacer8), + color: Colors.transparent, + child: Container( + width: _indexSize, + height: _indexSize, + decoration: isActive + ? BoxDecoration( + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusCircle), + color: TDTheme.of(context).brandColor7, + ) + : null, + child: Center( + child: TDText( + e, + forceVerticalCenter: true, + font: isActive ? TDTheme.of(context).fontMarkSmall : TDTheme.of(context).fontLinkSmall, + textColor: + isActive ? TDTheme.of(context).fontWhColor1 : TDTheme.of(context).fontGyColor1, + ), + ), + ), + ), + ], + ); + }, + ).toList(), + ); + }, + ), + ), + ), + ), + ), + ); + } + + void _changeSelect(Offset globalPosition) { + final newIndex = _fingerInsideContainer(globalPosition); + if (newIndex != null && newIndex != widget.activeIndex.value) { + final oldIndex = widget.activeIndex.value; + widget.activeIndex.value = newIndex; + _showTip = true; + widget.onSelect.call(newIndex, oldIndex); + } + } + + String? _fingerInsideContainer(Offset globalPosition) { + for (var entry in _containerKeys.entries) { + final renderBox = entry.value.currentContext?.findRenderObject() as RenderBox?; + if (renderBox != null) { + final localPosition = renderBox.globalToLocal(globalPosition); + final isIn = renderBox.hitTest(BoxHitTestResult(), position: localPosition); + if (isIn) { + return entry.key; + } + } + } + return null; + } + + void _hideTip() { + _hideTipTimer?.cancel(); + _hideTipTimer = Timer( + const Duration(seconds: 1), + () { + setState(() { + _showTip = false; + }); + }, + ); + } +} diff --git a/lib/src/components/input/input_view.dart b/tdesign-component/lib/src/components/input/input_view.dart similarity index 83% rename from lib/src/components/input/input_view.dart rename to tdesign-component/lib/src/components/input/input_view.dart index 984cb53a6..be8205cb4 100644 --- a/lib/src/components/input/input_view.dart +++ b/tdesign-component/lib/src/components/input/input_view.dart @@ -22,6 +22,12 @@ class TDInputView extends StatelessWidget { /// 最大输入行数 final int? maxLines; + + /// 最小输入行数 + final int? minLines; + + /// 最大输入长度 + final int? maxLength; /// 获取或者取消焦点使用 final FocusNode? focusNode; @@ -61,6 +67,12 @@ class TDInputView extends StatelessWidget { /// 文本对齐方向 final TextAlign? textAlign; + /// 键盘动作类型 + final TextInputAction? inputAction; + + /// 点击输入框外部区域回调 + final TapRegionCallback? onTapOutside; + const TDInputView( {Key? key, required this.textStyle, @@ -75,6 +87,8 @@ class TDInputView extends StatelessWidget { this.inputFormatters, this.inputDecoration, this.maxLines, + this.minLines, + this.maxLength, this.focusNode, this.hintTextStyle, this.cursorColor, @@ -82,7 +96,9 @@ class TDInputView extends StatelessWidget { this.contentPadding = EdgeInsets.zero, this.isCollapsed = false, this.textAlign, - this.controller}) + this.controller, + this.inputAction, + this.onTapOutside}) : super( key: key, ); @@ -93,6 +109,7 @@ class TDInputView extends StatelessWidget { inputFormatters: inputFormatters, readOnly: readOnly, keyboardType: inputType, + textInputAction: inputAction, autofocus: autofocus, obscureText: obscureText, onEditingComplete: onEditingComplete, @@ -102,8 +119,12 @@ class TDInputView extends StatelessWidget { focusNode: focusNode, cursorColor: cursorColor, maxLines: maxLines, + minLines: minLines, + maxLength: maxLength, + onTapOutside: onTapOutside, style: textStyle, textAlign: textAlign ?? TextAlign.start, + buildCounter: _buildCounter, decoration: inputDecoration ?? InputDecoration( hintText: hintText, @@ -122,4 +143,8 @@ class TDInputView extends StatelessWidget { ), ); } + + Widget? _buildCounter(BuildContext context, {required int currentLength, required bool isFocused, required int? maxLength}) { + return null; + } } diff --git a/tdesign-component/lib/src/components/input/td_input.dart b/tdesign-component/lib/src/components/input/td_input.dart new file mode 100644 index 000000000..bfb88f9ed --- /dev/null +++ b/tdesign-component/lib/src/components/input/td_input.dart @@ -0,0 +1,909 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../../tdesign_flutter.dart'; +import '../dialog/td_dialog_widget.dart'; + +enum TDInputType { normal, twoLine, longText, special, normalMaxTwoLine, cardStyle } + +enum TDInputSize { small, large } + +// 提供三种默认样式,也可以自定义decoration和上下文字。 +enum TDCardStyle { topText, topTextWithBlueBorder, errorStyle } + +class TDInput extends StatelessWidget { + TDInput({ + Key? key, + this.width, + this.textStyle, + this.backgroundColor, + this.decoration, + this.leftIcon, // leftIcon is default designed 24 in size. + this.leftLabel, + this.leftLabelStyle, + this.leftLabelSpace, + this.required, + this.readOnly = false, + this.autofocus = false, + this.obscureText = false, + this.onEditingComplete, + this.onSubmitted, + this.hintText, + this.inputType, + this.onChanged, + this.inputFormatters, + this.inputDecoration, + this.maxLines = 1, + this.focusNode, + this.controller, + this.cursorColor, + this.rightBtn, + this.hintTextStyle, + this.onBtnTap, + this.labelWidget, + this.leftInfoWidth, + this.leftContentSpace, + this.textInputBackgroundColor, + this.contentPadding, + this.type = TDInputType.normal, + this.size = TDInputSize.large, + this.maxLength = 500, + this.additionInfo = '', + this.additionInfoColor, + this.textAlign, + this.clearIconSize, + this.onClearTap, + this.needClear = true, + this.clearBtnColor, + this.contentAlignment = TextAlign.start, + this.rightWidget, + this.showBottomDivider = true, + this.cardStyle, + this.cardStyleTopText, + this.inputAction, + TDInputSpacer? spacer, + this.cardStyleBottomText, + this.onTapOutside, + }) : spacer = spacer ?? TDInputSpacer.generateDefault(); + + /// 输入框宽度(TDCardStyle时必须设置该参数) + final double? width; + + /// 输入框背景色 + final Color? backgroundColor; + + /// 输入框样式 + final Decoration? decoration; + + /// 输入框左侧文案 + final String? leftLabel; + + /// 输入框左侧文案间距 + final double? leftLabelSpace; + + /// 输入框内容左侧间距 + final double? leftContentSpace; + + /// 是否必填标志(红色*) + final bool? required; + + /// 带图标的输入框 + final Widget? leftIcon; + + /// leftLabel右侧组件,支持自定义 + final Widget? labelWidget; + + /// 是否只读 + final bool readOnly; + + /// 提示文案 + final String? hintText; + + /// 键盘类型,数字、字母 + final TextInputType? inputType; + + /// 输入文本变化时回调 + final ValueChanged? onChanged; + + /// 显示输入内容,如限制长度(LengthLimitingTextInputFormatter(6)) + final List? inputFormatters; + + /// controller 用户获取或者赋值输入内容 + final TextEditingController? controller; + + /// 最大输入行数 + final int maxLines; + + /// 获取或者取消焦点使用 + final FocusNode? focusNode; + + /// 是否自动获取焦点 + final bool autofocus; + + /// 是否隐藏输入的文字,一般用在密码输入框中 + final bool obscureText; + + /// 点击键盘完成按钮时触发的回调 + final VoidCallback? onEditingComplete; + + /// 点击键盘完成按钮时触发的回调, 参数值为输入的内容 + final ValueChanged? onSubmitted; + + /// 自定义输入框样式,默认圆角 + final InputDecoration? inputDecoration; + + /// 文本颜色 + final TextStyle? textStyle; + + /// 提示文本颜色,默认为文本颜色 + final TextStyle? hintTextStyle; + + /// 卡片模式上方文字 + final String? cardStyleTopText; + + /// 卡片模式下方文字 + final String? cardStyleBottomText; + + /// 文本框背景色 + final Color? textInputBackgroundColor; + + /// 游标颜色 + final Color? cursorColor; + + /// 清除按钮图标大小 + final double? clearIconSize; + + /// 右侧按钮 + final Widget? rightBtn; + + /// 右侧按钮点击 + final GestureTapCallback? onBtnTap; + + /// 右侧删除点击 + final GestureTapCallback? onClearTap; + + /// 是否需要右侧按钮变为删除 + final bool needClear; + + /// 右侧删除按钮颜色 + final Color? clearBtnColor; + + /// textInput内边距 + final EdgeInsetsGeometry? contentPadding; + + /// 输入框类型 + final TDInputType type; + + /// 卡片默认样式 + final TDCardStyle? cardStyle; + + /// 输入框规格 + final TDInputSize size; + + /// 输入框左侧的宽度(输入框有16dp的左侧padding,因而左侧部分不用考虑这16dp) + final double? leftInfoWidth; + + /// 最大字数限制 + final int? maxLength; + + /// 错误提示信息 + final String? additionInfo; + + /// 错误提示颜色 + final Color? additionInfoColor; + + /// 文字对齐方向 + final TextAlign? textAlign; + + /// 右侧自定义组件 特殊类型时生效 + final Widget? rightWidget; + + /// 是否展示底部分割线 + final bool showBottomDivider; + + /// 内容对齐方向 + final TextAlign contentAlignment; + + /// 左侧标签样式 设置该值是若出现像素溢出,请设置letterSpacing: 0 + final TextStyle? leftLabelStyle; + + /// 键盘动作类型 + final TextInputAction? inputAction; + + /// 组件各模块间间距 + final TDInputSpacer spacer; + + /// 左侧内容所占区域宽度 + double _leftLabelWidth = 0; + + /// 点击输入框外部区域回调 + final TapRegionCallback? onTapOutside; + + /// 获取输入框规格 + double getInputPadding() { + switch (size) { + case TDInputSize.small: + return 12; + case TDInputSize.large: + return 16; + } + } + + /// 计算输入框左侧信息总宽度 + /// 图标间距:若存在左侧图标 取间距配置值(默认4) + /// 图标宽度:24px图标尺寸 + 图标间距 + /// 标签宽度:通过文本测量函数获取精确文本渲染宽度 + /// 必填标识:当 required=true 时增加14px宽度(含4px间距) + /// 安全边距:最终增加4px防止截断 + double _calculateLeftInfoWidth(BuildContext context) { + final iconSpace = leftIcon != null ? (spacer.iconLabelSpace ?? 4) : 0; + final iconWidth = leftIcon != null ? 24 + iconSpace : 0; + final labelWidth = _measureTextWidth(leftLabel, leftLabelStyle, context); + final requiredWidth = (required ?? false) ? 14 : 0; + return iconWidth + labelWidth + requiredWidth + (leftContentSpace??4); + } + + /// 计算文本渲染宽度 + /// text: 待测量文本(null/空字符串返回0) + /// style: 文本样式(继承主题字体尺寸) + /// 排版控制:letterSpacing=0消除字间距,height=1.0避免行高影响 + /// 默认最大行数排版为1行 + double _measureTextWidth(String? text, TextStyle? style, BuildContext context) { + if (text == null || text.isEmpty) return 0; + final effectiveStyle = (style ?? TextStyle()).copyWith( + fontSize: TDTheme.of(context).fontBodyLarge?.size, + letterSpacing: 0, + height: 1.0, + ); + final textPainter = TextPainter( + text: TextSpan(text: text, style: effectiveStyle), + textDirection: TextDirection.ltr, + maxLines: 1, + )..layout(); + return textPainter.width + 2; + } + + Widget buildInputView(BuildContext context) { + _leftLabelWidth = _calculateLeftInfoWidth(context); + switch (type) { + case TDInputType.normal: + return buildNormalInput(context); + case TDInputType.twoLine: + return buildTwoLineInput(context); + case TDInputType.special: + return buildSpecialInput(context); + case TDInputType.longText: + return buildLongTextInput(context); + case TDInputType.normalMaxTwoLine: + return buildNormalInput(context); + case TDInputType.cardStyle: + return buildCardStyleInput(context); + } + } + + double _getBottomDividerMarginLeft() { + switch (type) { + case TDInputType.normal: + case TDInputType.twoLine: + case TDInputType.normalMaxTwoLine: + case TDInputType.cardStyle: + if (contentPadding != null && contentPadding is EdgeInsets) { + return (contentPadding as EdgeInsets).left; + } + return spacer.labelInputSpace ?? 16; + case TDInputType.special: + case TDInputType.longText: + if (contentPadding != null && contentPadding is EdgeInsets) { + return (contentPadding as EdgeInsets).left; + } + return 16; + } + } + + Widget buildNormalInput(BuildContext context) { + var cardStyleDecoration = _getCardStylePreDecoration(context); + var hasLeftWidget = leftLabel != null || leftIcon != null || (required ?? false); + return Stack( + alignment: Alignment.bottomCenter, + children: [ + Container( + alignment: Alignment.centerLeft, + color: (cardStyleDecoration != null || decoration != null) ? null : backgroundColor, + decoration: cardStyleDecoration ?? decoration, + child: Row( + crossAxisAlignment: additionInfo != '' ? CrossAxisAlignment.start : CrossAxisAlignment.center, + children: [ + Visibility( + visible: hasLeftWidget, + child: SizedBox( + width: leftLabelSpace ?? 16, + ), + ), + SizedBox( + width: _leftLabelWidth, + child: GestureDetector( + child: Row( + children: [ + Visibility( + visible: leftIcon != null, + child: SizedBox( + width: 24, + child: leftIcon ?? const SizedBox.shrink(), + ), + ), + Visibility( + visible: leftLabel != null, + child: Container( + constraints: BoxConstraints(maxWidth: _leftLabelWidth), + padding: EdgeInsets.only( + left: leftIcon != null ? (spacer.iconLabelSpace ?? 4) : 0, + top: getInputPadding(), + bottom: getInputPadding()), + child: TDText( + leftLabel, + maxLines: 1, + overflow: TextOverflow.visible, + style: leftLabelStyle ?? const TextStyle(letterSpacing: 0), + font: TDTheme.of(context).fontBodyLarge, + fontWeight: FontWeight.w400, + ), + ), + ), + Visibility( + visible: labelWidget != null, + child: labelWidget ?? const SizedBox.shrink(), + ), + Visibility( + visible: required ?? false, + child: Padding( + padding: const EdgeInsets.only(left: 4.0), + child: TDText( + '*', + maxLines: 1, + style: TextStyle(color: TDTheme.of(context).errorColor6), + font: TDTheme.of(context).fontBodyLarge, + fontWeight: FontWeight.w400, + ), + )), + ], + ), + ), + ), + Expanded( + flex: 1, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TDInputView( + textStyle: textStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor1), + readOnly: readOnly, + autofocus: autofocus, + obscureText: obscureText, + onEditingComplete: onEditingComplete, + onSubmitted: onSubmitted, + hintText: hintText, + inputType: inputType, + onChanged: onChanged, + onTapOutside: onTapOutside, + inputFormatters: inputFormatters, + inputDecoration: inputDecoration, + maxLines: maxLines, + maxLength: maxLength, + focusNode: focusNode, + isCollapsed: true, + textAlign: contentAlignment, + hintTextStyle: hintTextStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor3), + cursorColor: cursorColor, + textInputBackgroundColor: textInputBackgroundColor, + controller: controller, + contentPadding: contentPadding ?? + EdgeInsets.only( + left: spacer.labelInputSpace ?? 16, + right: spacer.inputRightSpace != null ? spacer.inputRightSpace! / 2 : 16, + bottom: additionInfo != '' ? 4 : getInputPadding(), + top: getInputPadding()), + inputAction: inputAction, + ), + Visibility( + child: Container( + width: double.infinity, + padding: EdgeInsets.only( + left: spacer.additionInfoSpace ?? 16, + right: TextAlign.end == contentAlignment ? 8 : 0, + bottom: getInputPadding()), + child: TDText( + additionInfo, + font: TDTheme.of(context).fontBodySmall, + textAlign: contentAlignment != TextAlign.center ? contentAlignment : TextAlign.start, + textColor: additionInfoColor ?? TDTheme.of(context).fontGyColor3, + ), + ), + visible: additionInfo != '', + ) + ], + ), + ), + Visibility( + visible: rightWidget != null, + child: Container( + margin: EdgeInsets.only(top: getInputPadding(), bottom: getInputPadding(), right: 16), + child: rightWidget, + ), + ), + Visibility( + visible: controller != null && controller!.text.isNotEmpty && needClear && rightWidget == null, + child: GestureDetector( + child: Container( + margin: EdgeInsets.only( + left: spacer.inputRightSpace != null ? spacer.inputRightSpace! / 2 : 8, + right: spacer.rightSpace ?? 16, + top: additionInfo != '' ? getInputPadding() : 0), + child: Icon( + size: clearIconSize, + TDIcons.close_circle_filled, + color: clearBtnColor ?? TDTheme.of(context).fontGyColor3, + ), + ), + onTap: onClearTap ?? + () { + controller?.text = ''; + }), + replacement: Visibility( + visible: rightBtn != null, + child: GestureDetector( + onTap: onBtnTap, + child: Container( + margin: EdgeInsets.only( + left: spacer.inputRightSpace != null ? spacer.inputRightSpace! / 2 : 8, + right: spacer.rightSpace ?? 16, + top: additionInfo != '' ? getInputPadding() : 0), + child: rightBtn, + ), + ), + ), + ), + ], + ), + ), + if (showBottomDivider) + Visibility( + visible: type != TDInputType.cardStyle, + child: TDDivider( + margin: EdgeInsets.only( + left: _getBottomDividerMarginLeft(), + ), + ), + ), + ], + ); + } + + BoxDecoration? _getCardStylePreDecoration(BuildContext context) { + var cardStyleDecoration; + if (type == TDInputType.cardStyle) { + switch (cardStyle) { + case TDCardStyle.topText: + cardStyleDecoration = BoxDecoration( + color: Colors.white, + border: Border.all(color: TDTheme.of(context).grayColor4), + borderRadius: BorderRadius.circular(6)); + break; + case TDCardStyle.topTextWithBlueBorder: + cardStyleDecoration = BoxDecoration( + color: Colors.white, + border: Border.all(color: TDTheme.of(context).brandNormalColor, width: 1.5), + borderRadius: BorderRadius.circular(6)); + break; + case TDCardStyle.errorStyle: + cardStyleDecoration = BoxDecoration( + color: Colors.white, + border: Border.all(color: TDTheme.of(context).errorColor6, width: 1.5), + borderRadius: BorderRadius.circular(6)); + break; + default: + cardStyleDecoration = BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(6)); + break; + } + } + return cardStyleDecoration; + } + + Widget buildTwoLineInput(BuildContext context) { + return Container( + alignment: Alignment.centerLeft, + color: decoration != null ? null : backgroundColor, + decoration: decoration, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Visibility( + visible: leftLabel != null, + child: Row( + children: [ + Visibility( + visible: leftLabel != null, + child: Container( + constraints: BoxConstraints(maxWidth: _leftLabelWidth + (leftLabelSpace ?? 12)), + padding: EdgeInsets.only(left: leftLabelSpace ?? 12.0, top: 10.0), + child: Column( + children: [ + TDText( + leftLabel, + maxLines: 2, + style: leftLabelStyle ?? const TextStyle(letterSpacing: 0), + font: TDTheme.of(context).fontBodyLarge, + fontWeight: FontWeight.w400, + ), + ], + ), + ), + ), + Visibility( + visible: labelWidget != null, + child: labelWidget ?? const SizedBox.shrink(), + ), + Visibility( + visible: required ?? false, + child: Padding( + padding: const EdgeInsets.only(left: 1.0), + child: TDText( + '*', + maxLines: 1, + style: TextStyle(color: TDTheme.of(context).errorColor6), + font: TDTheme.of(context).fontBodyLarge, + fontWeight: FontWeight.w400, + ), + )), + ], + )), + Container( + padding: const EdgeInsets.only(bottom: 12, top: 7), + alignment: Alignment.center, + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Visibility( + visible: labelWidget != null, + child: labelWidget ?? const SizedBox.shrink(), + ), + Expanded( + flex: 1, + child: TDInputView( + textStyle: textStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor1), + readOnly: readOnly, + autofocus: autofocus, + obscureText: obscureText, + onEditingComplete: onEditingComplete, + onSubmitted: onSubmitted, + hintText: hintText, + inputType: inputType, + onChanged: onChanged, + textAlign: textAlign, + inputFormatters: inputFormatters, + inputDecoration: inputDecoration, + isCollapsed: true, + maxLines: maxLines, + focusNode: focusNode, + hintTextStyle: hintTextStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor3), + cursorColor: cursorColor, + textInputBackgroundColor: textInputBackgroundColor, + controller: controller, + contentPadding: contentPadding ?? + EdgeInsets.only( + left: spacer.labelInputSpace ?? 16, + right: spacer.inputRightSpace != null ? spacer.inputRightSpace! / 2 : 8, + ), + inputAction: inputAction, + ), + ), + Visibility( + visible: controller != null && controller!.text.isNotEmpty && needClear, + child: GestureDetector( + child: Container( + margin: EdgeInsets.only( + left: spacer.inputRightSpace != null ? spacer.inputRightSpace! / 2 : 8, + right: spacer.rightSpace ?? 16, + ), + child: Icon( + size: clearIconSize, + TDIcons.close_circle_filled, + color: clearBtnColor ?? TDTheme.of(context).fontGyColor3, + ), + ), + onTap: onClearTap, + ), + replacement: Visibility( + visible: rightBtn != null, + child: GestureDetector( + onTap: onBtnTap, + child: Container( + margin: EdgeInsets.only( + left: spacer.inputRightSpace != null ? spacer.inputRightSpace! / 2 : 8, + right: spacer.rightSpace ?? 16, + ), + child: rightBtn, + ), + ), + ), + ), + ], + ), + ), + ], + ), + if (showBottomDivider) + TDDivider( + margin: EdgeInsets.only( + left: _getBottomDividerMarginLeft(), + ), + ), + ], + ), + ); + } + + Widget buildLongTextInput(BuildContext context) { + return Container( + alignment: Alignment.centerLeft, + color: decoration != null ? null : backgroundColor, + decoration: decoration, + height: leftLabel != null ? 197 : 148, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Visibility( + visible: leftLabel != null, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(left: 16, top: getInputPadding(), bottom: getInputPadding()), + child: TDText( + leftLabel, + maxLines: 2, + fontWeight: FontWeight.w400, + )), + if (showBottomDivider) + TDDivider( + margin: EdgeInsets.only( + left: _getBottomDividerMarginLeft(), + ), + ), + ], + ), + ), + Expanded( + flex: 1, + child: TDInputView( + textStyle: textStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor1), + readOnly: readOnly, + autofocus: autofocus, + obscureText: obscureText, + onEditingComplete: onEditingComplete, + onSubmitted: onSubmitted, + hintText: hintText, + inputType: inputType, + textAlign: textAlign, + onChanged: onChanged, + inputFormatters: inputFormatters ?? [LengthLimitingTextInputFormatter(maxLength)], + inputDecoration: inputDecoration, + maxLines: maxLines, + focusNode: focusNode, + hintTextStyle: hintTextStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor3), + cursorColor: cursorColor, + textInputBackgroundColor: textInputBackgroundColor, + controller: controller, + contentPadding: contentPadding ?? const EdgeInsets.only(left: 16, right: 16, top: 12, bottom: 12), + inputAction: inputAction, + ), + ), + Container( + alignment: Alignment.bottomRight, + padding: const EdgeInsets.only(left: 16, right: 16, bottom: 12), + child: TDText( + '${controller?.text.length}/${maxLength}', + font: TDTheme.of(context).fontBodySmall, + textColor: TDTheme.of(context).fontGyColor3, + ), + ), + ], + ), + ); + } + + Widget buildSpecialInput(BuildContext context) { + return Stack( + alignment: Alignment.bottomCenter, + children: [ + Container( + alignment: Alignment.centerLeft, + color: decoration != null ? null : backgroundColor, + decoration: decoration, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Visibility( + visible: leftLabel != null, + child: Padding( + padding: + EdgeInsets.only(left: leftLabelSpace ?? 16, top: getInputPadding(), bottom: getInputPadding()), + child: leftInfoWidth != null + ? SizedBox( + width: _leftLabelWidth, + child: TDText( + leftLabel, + maxLines: 1, + font: TDTheme.of(context).fontBodyLarge, + fontWeight: FontWeight.w400, + ), + ) + : TDText( + leftLabel, + maxLines: 1, + font: TDTheme.of(context).fontBodyLarge, + fontWeight: FontWeight.w400, + ), + ), + ), + Visibility( + visible: labelWidget != null, + child: labelWidget ?? const SizedBox.shrink(), + ), + Expanded( + flex: 1, + child: Padding( + padding: EdgeInsets.only(left: spacer.labelInputSpace!), + child: TDInputView( + textStyle: textStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor1), + readOnly: readOnly, + autofocus: autofocus, + obscureText: obscureText, + onEditingComplete: onEditingComplete, + onSubmitted: onSubmitted, + hintText: hintText, + inputType: inputType, + onChanged: onChanged, + inputFormatters: inputFormatters, + inputDecoration: inputDecoration, + maxLines: maxLines, + focusNode: focusNode, + isCollapsed: true, + hintTextStyle: hintTextStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor3), + cursorColor: cursorColor, + textInputBackgroundColor: textInputBackgroundColor, + controller: controller, + textAlign: textAlign, + contentPadding: contentPadding ?? + EdgeInsets.only( + right: spacer.inputRightSpace!, bottom: getInputPadding(), top: getInputPadding()), + inputAction: inputAction, + ), + ), + ), + Visibility( + visible: rightWidget != null, + child: Container( + margin: EdgeInsets.only(top: getInputPadding(), bottom: getInputPadding(), right: spacer.rightSpace!), + child: rightWidget, + ), + ), + ], + ), + ), + if (showBottomDivider) + Visibility( + child: TDDivider( + margin: EdgeInsets.only( + left: _getBottomDividerMarginLeft(), + ), + ), + ), + ], + ); + } + + Size getTextSize(String text, [TextStyle? style]) { + var painter = TextPainter( + text: TextSpan(text: text, style: style), + textDirection: TextDirection.ltr, + maxLines: 1, + ellipsis: '...', + ); + painter.layout(); + return painter.size; + } + + @override + Widget build(BuildContext context) { + var screenWidth = MediaQuery.of(context).size.width; + return SizedBox( + child: buildInputView(context), + width: width ?? screenWidth, + ); + } + + Widget buildCardStyleInput(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Visibility( + visible: cardStyleTopText != null, + child: Column( + children: [ + Text( + cardStyleTopText ?? '', + style: TextStyle( + fontSize: TDTheme.of(context).fontBodyMedium!.size, + height: TDTheme.of(context).fontBodyMedium!.height), + ), + const SizedBox( + height: 8, + ), + ], + ), + ), + buildNormalInput(context), + Visibility( + visible: cardStyleBottomText != null, + child: Column( + children: [ + const SizedBox( + height: 8, + ), + Text( + cardStyleBottomText ?? '', + style: TextStyle( + color: TDTheme.of(context).errorColor6, + fontSize: TDTheme.of(context).fontBodySmall!.size, + height: TDTheme.of(context).fontBodySmall!.height), + ), + ], + ), + ), + ], + ); + } +} + +/// 中文算作两个字符类型的TextInputFormatter +class Chinese2Formatter extends TextInputFormatter { + final int maxLength; + + Chinese2Formatter(this.maxLength); + + final _regExp = r'^[\u4E00-\u9FA5A-Za-z0-9_]+$'; + + @override + TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) { + var newValueLength = newValue.text.length; + var count = 0; + if (newValueLength == 0) { + return newValue; + } + if (maxLength > 0) { + for (var i = 0; i < newValueLength; i++) { + if (newValue.text.codeUnitAt(i) > 122) { + ///中文字符按照2个计算 + count++; + } + if (i > 0 && count + i > maxLength - 1) { + var text = newValue.text.substring(0, i); + return newValue.copyWith( + text: text, + composing: TextRange.empty, + selection: TextSelection.fromPosition(TextPosition(offset: i, affinity: TextAffinity.downstream))); + } + } + } + if (newValueLength > 0 && RegExp(_regExp).firstMatch(newValue.text) != null) { + if (newValueLength + count <= maxLength) { + return newValue; + } + } + return oldValue; + } +} diff --git a/tdesign-component/lib/src/components/input/td_input_spacer.dart b/tdesign-component/lib/src/components/input/td_input_spacer.dart new file mode 100644 index 000000000..23e0bae12 --- /dev/null +++ b/tdesign-component/lib/src/components/input/td_input_spacer.dart @@ -0,0 +1,23 @@ +class TDInputSpacer { + TDInputSpacer({ + this.iconLabelSpace, + this.labelInputSpace, + this.inputRightSpace, + this.rightSpace, + this.additionInfoSpace, + }); + + double? iconLabelSpace; + double? labelInputSpace; + double? inputRightSpace; + double? rightSpace; + double? additionInfoSpace; + + TDInputSpacer.generateDefault() { + iconLabelSpace = 4; + labelInputSpace = 16; + inputRightSpace = 16; + rightSpace = 16; + additionInfoSpace = 16; + } +} \ No newline at end of file diff --git a/tdesign-component/lib/src/components/link/td_link.dart b/tdesign-component/lib/src/components/link/td_link.dart new file mode 100644 index 000000000..7f967fb26 --- /dev/null +++ b/tdesign-component/lib/src/components/link/td_link.dart @@ -0,0 +1,280 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +/// 限制Function类型,防止传递错误的Function,导致参数对不上 +typedef LinkClick = Function(Uri? uri); + +enum TDLinkType { + basic, + withUnderline, + withPrefixIcon, + withSuffixIcon, +} + +enum TDLinkStyle { + primary, + defaultStyle, + danger, + warning, + success, +} + +enum TDLinkState { + normal, + active, + disabled, +} + +enum TDLinkSize { + small, + medium, + large, +} + +class TDLink extends StatelessWidget { + const TDLink({ + Key? key, + required this.label, + this.uri, + this.prefixIcon, + this.suffixIcon, + this.linkClick, + this.type = TDLinkType.basic, + this.style = TDLinkStyle.defaultStyle, + this.state = TDLinkState.normal, + this.size = TDLinkSize.medium, + this.color, + this.iconSize, + this.fontSize, + this.leftGapWithIcon, + this.rightGapWithIcon, + }) : super(key: key); + + /// link 展示的文本 + final String label; + + /// link 跳转的uri + final Uri? uri; + + /// link 类型 + final TDLinkType type; + + /// link 风格 + final TDLinkStyle style; + + /// link 状态 + final TDLinkState state; + + /// link 大小 + final TDLinkSize size; + + /// 前置 icon + final Icon? prefixIcon; + + /// 后置 icon + final Icon? suffixIcon; + + /// link 文本的颜色,如果不设置则根据状态和风格进行计算 + final Color? color; + + /// link icon 大小,如果不设置则根据状态和风格进行计算 + final double? iconSize; + + /// link 文本的字体大小,如果不设置则根据状态和风格进行计算 + final double? fontSize; + + /// 前置icon和文本之间的间隔,如果不设置则根据状态和风格进行计算 + final double? leftGapWithIcon; + + /// 后置icon和文本之间的间隔,如果不设置则根据状态和风格进行计算 + final double? rightGapWithIcon; + + /// link 被点击之后所采取的动作,会将uri当做参数传入到该方法当中 + final LinkClick? linkClick; + + @override + Widget build(BuildContext context) { + if (type == TDLinkType.withPrefixIcon) { + return Row(children: [ + prefixIcon == null ? _getDefaultIcon(context) : prefixIcon!, + SizedBox( + width: _getLeftGapSize(context), + ), + _buildLink(context), + ]); + } else if (type == TDLinkType.withSuffixIcon) { + return Row(children: [ + _buildLink(context), + SizedBox( + width: _getRightGapSize(context), + ), + suffixIcon == null ? _getDefaultIcon(context) : suffixIcon!, + ]); + } + + return _buildLink(context); + } + + /// 提取成方法,允许业务定义自己的TDLinkConfiguration + TDLinkConfiguration? getConfiguration(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + Color getColor(BuildContext context) { + if (color != null) { + return color!; + } + // to refactor: use map instead of multi level switch + switch (state) { + case TDLinkState.normal: + switch (style) { + case TDLinkStyle.primary: + return TDTheme.of(context).brandNormalColor; + case TDLinkStyle.danger: + return TDTheme.of(context).errorColor6; + case TDLinkStyle.warning: + return TDTheme.of(context).warningColor5; + case TDLinkStyle.success: + return TDTheme.of(context).successColor5; + case TDLinkStyle.defaultStyle: + return TDTheme.of(context).fontGyColor1; + } + + case TDLinkState.active: + switch (style) { + case TDLinkStyle.primary: + return TDTheme.of(context).brandClickColor; + case TDLinkStyle.danger: + return TDTheme.of(context).errorColor7; + case TDLinkStyle.warning: + return TDTheme.of(context).warningColor6; + case TDLinkStyle.success: + return TDTheme.of(context).successColor6; + case TDLinkStyle.defaultStyle: + return TDTheme.of(context).brandClickColor; + } + case TDLinkState.disabled: + switch (style) { + case TDLinkStyle.primary: + return TDTheme.of(context).brandDisabledColor; + case TDLinkStyle.danger: + return TDTheme.of(context).errorDisabledColor; + case TDLinkStyle.warning: + return TDTheme.of(context).warningDisabledColor; + case TDLinkStyle.success: + return TDTheme.of(context).successDisabledColor; + case TDLinkStyle.defaultStyle: + return TDTheme.of(context).fontGyColor4; + } + } + } + + Widget _getDefaultIcon(BuildContext context) { + return Icon( + type == TDLinkType.withPrefixIcon ? TDIcons.link : TDIcons.jump, + size: _getIconSize(context), + color: getColor(context), + ); + } + + Widget _buildLink(BuildContext context) { + return InkWell( + onTap: () { + if (state == TDLinkState.disabled) { + return; + } + if (linkClick != null) { + linkClick!(uri); + } else { + var tdLinkConfig = getConfiguration(context); + + if (tdLinkConfig != null && tdLinkConfig.linkClick != null) { + tdLinkConfig.linkClick!(uri); + } + } + }, + child: TDText( + label, + style: TextStyle( + fontSize: _getFontSize(context), + color: getColor(context), + decoration: type == TDLinkType.withUnderline + ? TextDecoration.underline + : null, + decorationColor: getColor(context), + ), + forceVerticalCenter: true, + )); + } + + double _getIconSize(BuildContext context) { + if (iconSize != null) { + return iconSize!; + } + switch (size) { + case TDLinkSize.large: + return 18; + case TDLinkSize.small: + return 14; + case TDLinkSize.medium: + return 16; + } + } + + double _getFontSize(BuildContext context) { + if (fontSize != null) { + return fontSize!; + } + switch (size) { + case TDLinkSize.large: + return 16; + case TDLinkSize.small: + return 12; + case TDLinkSize.medium: + return 14; + } + } + + double _getLeftGapSize(BuildContext context) { + if (leftGapWithIcon != null) { + return leftGapWithIcon!; + } + switch (size) { + case TDLinkSize.large: + return 14.64; + case TDLinkSize.small: + return 6.05; + case TDLinkSize.medium: + return 6.34; + } + } + + double _getRightGapSize(BuildContext context) { + if (rightGapWithIcon != null) { + return rightGapWithIcon!; + } + switch (size) { + case TDLinkSize.large: + return 15.37; + case TDLinkSize.small: + return 6.63; + case TDLinkSize.medium: + return 7; + } + } +} + +/// 存储可以自定义TDLink跳转算法的控件 +class TDLinkConfiguration extends InheritedWidget { + /// 统一跳转的函数 + final LinkClick? linkClick; + + const TDLinkConfiguration({this.linkClick, Key? key, required Widget child}) + : super(key: key, child: child); + + @override + bool updateShouldNotify(covariant TDLinkConfiguration oldWidget) { + return linkClick != oldWidget.linkClick; + } +} diff --git a/lib/src/components/loading/td_activity_indicator.dart b/tdesign-component/lib/src/components/loading/td_activity_indicator.dart similarity index 84% rename from lib/src/components/loading/td_activity_indicator.dart rename to tdesign-component/lib/src/components/loading/td_activity_indicator.dart index 4008a73fb..ac2356f1b 100644 --- a/lib/src/components/loading/td_activity_indicator.dart +++ b/tdesign-component/lib/src/components/loading/td_activity_indicator.dart @@ -2,8 +2,7 @@ import 'dart:math' as math; import 'package:flutter/widgets.dart'; -import '../../../td_export.dart'; - +import '../../../tdesign_flutter.dart'; const double _kDefaultIndicatorRadius = 10.0; @@ -21,6 +20,7 @@ class TDCupertinoActivityIndicator extends StatefulWidget { this.animating = true, this.radius = _kDefaultIndicatorRadius, this.activeColor, + this.duration = 2000, }) : assert(radius > 0.0), progress = 1.0, super(key: key); @@ -45,12 +45,15 @@ class TDCupertinoActivityIndicator extends StatefulWidget { final Color? activeColor; + final int duration; + @override _TDCupertinoActivityIndicatorState createState() => _TDCupertinoActivityIndicatorState(); } -class _TDCupertinoActivityIndicatorState extends State +class _TDCupertinoActivityIndicatorState + extends State with SingleTickerProviderStateMixin { late AnimationController _controller; @@ -58,7 +61,7 @@ class _TDCupertinoActivityIndicatorState extends State position; @@ -174,4 +178,4 @@ class _CupertinoActivityIndicatorPainter extends CustomPainter { oldPainter.activeColor != activeColor || oldPainter.progress != progress; } -} \ No newline at end of file +} diff --git a/tdesign-component/lib/src/components/loading/td_circle_indicator.dart b/tdesign-component/lib/src/components/loading/td_circle_indicator.dart new file mode 100644 index 000000000..e8b6b8e4e --- /dev/null +++ b/tdesign-component/lib/src/components/loading/td_circle_indicator.dart @@ -0,0 +1,119 @@ +/* + * Created by haozhicao@tencent.com on 6/28/22. + * td_circle_indicator.dart + * + */ + +import 'dart:math'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +class TDCircleIndicator extends StatefulWidget { + const TDCircleIndicator({ + Key? key, + this.color, + this.size = 20.0, + this.lineWidth = 3.0, + this.duration = 1000, + }) : super(key: key); + + final Color? color; + final double size; + final double lineWidth; + final int duration; + + @override + _TDCircleIndicatorState createState() => _TDCircleIndicatorState(); +} + +class _TDCircleIndicatorState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _animation1; + + @override + void initState() { + super.initState(); + + _controller = AnimationController( + vsync: this, duration: Duration(milliseconds: widget.duration)) + ..addListener(() => setState(() {})) + ..repeat(); + _animation1 = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( + parent: _controller, + curve: const Interval(0.0, 1.0, curve: Curves.linear))); + } + + @override + void didUpdateWidget(covariant TDCircleIndicator oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.duration != oldWidget.duration) { + _controller.duration = Duration(milliseconds: widget.duration); + _controller.repeat(); + } + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + var value = (_animation1.value) * 2 * pi; + var paintColor = widget.color ?? TDTheme.of(context).brandNormalColor; + return Transform( + transform: Matrix4.identity()..rotateZ(value), + alignment: FractionalOffset.center, + child: SizedBox.fromSize( + size: Size.square(widget.size), + child: CustomPaint( + painter: _CirclePaint(color: paintColor, width: widget.lineWidth), + ), + ), + ); + } +} + +class _CirclePaint extends CustomPainter { + final Color color; + final double width; + + _CirclePaint({required this.color, required this.width}); + + final _paint = Paint()..style = PaintingStyle.stroke; + + @override + void paint(Canvas canvas, Size size) { + var minLength = min(size.width, size.height); + _paint.strokeWidth = width; + _paint.shader = ui.Gradient.sweep(Offset(size.width / 2, size.height / 2), + [const Color(0x01ffffff), color]); + if (minLength == size.width) { + // strokeWidth是居中位置的,需要减去width/2,使其向内绘制 + canvas.drawArc( + Rect.fromLTWH(width / 2, (size.height - size.width) / 2 + width / 2, + size.width - width, size.width - width), + 0, + pi * 2, + false, + _paint); + } else { + canvas.drawArc( + Rect.fromLTWH((size.width - size.height) / 2 + width / 2, width / 2, + size.height - width, size.height - width), + 0, + pi * 2, + false, + _paint); + } + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return true; + } +} diff --git a/tdesign-component/lib/src/components/loading/td_loading.dart b/tdesign-component/lib/src/components/loading/td_loading.dart new file mode 100644 index 000000000..5c8c5c098 --- /dev/null +++ b/tdesign-component/lib/src/components/loading/td_loading.dart @@ -0,0 +1,199 @@ +/* + * Created by haozhicao@tencent.com on 6/29/22. + * td_loading.dart + * + */ + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import 'td_activity_indicator.dart'; +import 'td_point_indicator.dart'; + +/// Loading 尺寸 +enum TDLoadingSize { + small, + medium, + large, +} + +/// Loading的图标 +enum TDLoadingIcon { + circle, + point, + activity, +} + +class TDLoading extends StatelessWidget { + const TDLoading({ + Key? key, + required this.size, + this.icon = TDLoadingIcon.circle, + this.iconColor, + this.axis = Axis.vertical, + this.text, + this.refreshWidget, + this.customIcon, + this.textColor = Colors.black, + this.duration = 2000, + }) : super(key: key); + + /// 尺寸 + final TDLoadingSize size; + /// 图标,支持圆形、点状、菊花状 + final TDLoadingIcon? icon; + /// 图标颜色 + final Color? iconColor; + /// 文案 + final String? text; + /// 失败刷新组件 + final Widget? refreshWidget; + /// 文案颜色 + final Color textColor; + /// 文案和图标相对方向 + final Axis axis; + /// 自定义图标,优先级高于icon + final Widget? customIcon; + /// 一次刷新的时间,控制动画速度 + final int duration; + + int get _innerDuration => duration > 0 ? duration : 1; + + @override + Widget build(BuildContext context) { + return Wrap( + children: [_contentWidget()], + ); + } + + Widget _contentWidget() { + if (icon == null) { + return textWidget(); + } else { + Widget? indicator; + if (customIcon != null) { + indicator = customIcon!; + } else { + switch (icon!) { + case TDLoadingIcon.activity: + indicator = TDCupertinoActivityIndicator( + activeColor: iconColor, + radius: size == TDLoadingSize.small + ? 10 + : (size == TDLoadingSize.medium ? 11 : 13), + duration: _innerDuration, + ); + break; + case TDLoadingIcon.circle: + indicator = _getCircleIndicator(); + break; + case TDLoadingIcon.point: + indicator = TDPointBounceIndicator( + color: iconColor, + size: size == TDLoadingSize.small + ? 12 + : (size == TDLoadingSize.medium ? 16 : 20), + duration: _innerDuration, + ); + break; + default: + indicator = _getCircleIndicator(); + break; + } + } + + if (text == null) { + return indicator; + } else if (axis == Axis.vertical) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + indicator, + SizedBox( + height: _getPaddingWidth(), + ), + textWidget(), + ]); + } else { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + indicator, + SizedBox( + width: _getPaddingWidth(), + ), + textWidget() + ]); + } + } + } + + Widget _getCircleIndicator() { + switch (size) { + case TDLoadingSize.large: + return TDCircleIndicator( + color: iconColor, + size: 24, + lineWidth: 3 * 4 / 3, // 根据small等等比缩放 + duration: _innerDuration, + ); + case TDLoadingSize.medium: + return TDCircleIndicator( + color: iconColor, + size: 21, + lineWidth: 3 * 7 / 6, // 根据small等等比缩放 + duration: _innerDuration, + ); + case TDLoadingSize.small: + return TDCircleIndicator( + color: iconColor, + size: 18, // 设计稿框位24,图形宽位19.5,推导lineWidth为3时,size位18 + lineWidth: 3, + duration: _innerDuration, + ); + } + } + + double _getPaddingWidth() { + switch (size) { + case TDLoadingSize.large: + return 10; + case TDLoadingSize.medium: + return 8; + case TDLoadingSize.small: + return 6; + } + } + + Font fitFont() { + switch (size) { + case TDLoadingSize.large: + return TDTheme.of().fontBodyLarge ?? Font(size: 16, lineHeight: 24); + case TDLoadingSize.medium: + return TDTheme.of().fontBodyMedium ?? Font(size: 14, lineHeight: 22); + case TDLoadingSize.small: + return TDTheme.of().fontBodySmall ?? Font(size: 12, lineHeight: 20); + } + } + + Widget textWidget() { + Widget result = TDText( + text, + textColor: textColor, + fontWeight: FontWeight.w400, + font: fitFont(), + textAlign: TextAlign.center, + ); + if(refreshWidget != null){ + result = Row( + mainAxisSize: MainAxisSize.min, + children: [ + result, + const SizedBox(width: 8,), + refreshWidget!, + ], + ); + } + return result; + } +} diff --git a/tdesign-component/lib/src/components/loading/td_loading_controller.dart b/tdesign-component/lib/src/components/loading/td_loading_controller.dart new file mode 100644 index 000000000..1a0c79e41 --- /dev/null +++ b/tdesign-component/lib/src/components/loading/td_loading_controller.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import '../../util/context_extension.dart'; +import 'td_loading.dart'; + +class TDLoadingController { + static BuildContext? _context; + static OverlayEntry? _overlayEntry; + + static bool _isShowing = false; + + // 展示 + static void show(BuildContext context, + {Widget? child, + TDLoadingSize size = TDLoadingSize.medium, + TDLoadingIcon? icon = TDLoadingIcon.circle, + Color? iconColor, + String? text, + Widget? refreshWidget, + Color textColor = Colors.black, + Axis axis = Axis.vertical, + Widget? customIcon, + int duration = 2000}) { + + if (_isShowing) { + print('warn: TDLoading is showing!'); + return; + } + + _overlayEntry = OverlayEntry(builder: (context) { + return Center( + child: child ?? + TDLoading( + size: size, + icon: icon, + customIcon: customIcon, + text: text ?? context.resource.loading, + textColor: textColor, + refreshWidget: refreshWidget, + duration: duration, + iconColor: iconColor, + axis: axis, + ), + ); + }); + + _context = context; + if (_context == null || _overlayEntry == null) { + print('error: TDLoading is not init!:${_context} ${_overlayEntry}'); + return; + } + _isShowing = true; + Overlay.of(_context!).insert(_overlayEntry!); + } + + // 消失 + static void dismiss() { + if (_isShowing) { + if (_overlayEntry != null) { + _overlayEntry?.remove(); + _overlayEntry = null; + } + _isShowing = false; + } + } +} diff --git a/lib/src/components/loading/td_point_indicator.dart b/tdesign-component/lib/src/components/loading/td_point_indicator.dart similarity index 82% rename from lib/src/components/loading/td_point_indicator.dart rename to tdesign-component/lib/src/components/loading/td_point_indicator.dart index e8323fbc8..8b99b455b 100644 --- a/lib/src/components/loading/td_point_indicator.dart +++ b/tdesign-component/lib/src/components/loading/td_point_indicator.dart @@ -12,13 +12,13 @@ class TDPointBounceIndicator extends StatefulWidget { Key? key, this.color, this.size = 20.0, - this.duration = const Duration(milliseconds: 1400), + this.duration = 1400, this.controller, }) : super(key: key); final Color? color; final double size; - final Duration duration; + final int duration; final AnimationController? controller; @override @@ -34,10 +34,20 @@ class _TDPointBounceIndicatorState extends State super.initState(); _controller = (widget.controller ?? - AnimationController(vsync: this, duration: widget.duration)) + AnimationController( + vsync: this, duration: Duration(milliseconds: widget.duration))) ..repeat(); } + @override + void didUpdateWidget(covariant TDPointBounceIndicator oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.duration != oldWidget.duration) { + _controller.duration = Duration(milliseconds: widget.duration); + _controller.repeat(); + } + } + @override void dispose() { if (widget.controller == null) { diff --git a/tdesign-component/lib/src/components/message/td_message.dart b/tdesign-component/lib/src/components/message/td_message.dart new file mode 100644 index 000000000..7416188a1 --- /dev/null +++ b/tdesign-component/lib/src/components/message/td_message.dart @@ -0,0 +1,475 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/src/scheduler/binding.dart'; + +import '../../../tdesign_flutter.dart'; + +//链接设置 +class MessageLink { + MessageLink({ + required this.name, + required this.uri, + this.color, + }); + + /// 名称 + final String name; + + /// 资源链接 + final Uri? uri; + + /// 颜色 + final Color? color; +} + +// 跑马灯配置 +class MessageMarquee { + MessageMarquee({this.speed, this.loop, this.delay}); + + /// 速度 + final int? speed; + + /// 循环次数 + final int? loop; + + /// 延迟时间(毫秒) + final int? delay; +} + +// 定义消息主题枚举 +enum MessageTheme { + /// 普通通知 + info, + + /// 成功通知 + success, + + /// 警示通知 + warning, + + /// 错误通知 + error +} + +// TDMessage 组件 +class TDMessage extends StatefulWidget { + const TDMessage({ + Key? key, + this.closeBtn, + this.content, + this.duration = 3000, + this.icon = true, + this.link, + this.marquee, + this.offset, + this.theme = MessageTheme.info, + this.visible = true, + this.onCloseBtnClick, + this.onDurationEnd, + this.onLinkClick, + }) : super(key: key); + + /// 通知内容 + final String? content; + + /// 消息内置计时器 + final int? duration; + + /// 是否显示 + final bool? visible; + + /// 自定义消息前面的图标 + final dynamic icon; + + /// 链接名称 + final dynamic link; + + /// 关闭按钮 + final dynamic closeBtn; + + /// 跑马灯效果 + final MessageMarquee? marquee; + + /// 相对于 placement 的偏移量 + final List? offset; + + /// 消息组件风格 info/success/warning/error + final MessageTheme? theme; + + /// 点击关闭按钮触发 + final VoidCallback? onCloseBtnClick; + + /// 计时结束后触发 + final VoidCallback? onDurationEnd; + + /// 点击链接文本时触发 + final VoidCallback? onLinkClick; + + @override + _TDMessageState createState() => _TDMessageState(); + + static void showMessage({ + required BuildContext context, + String? content, + bool? visible, + int? duration, + dynamic closeBtn, + dynamic icon, + dynamic link, + MessageMarquee? marquee, + List? offset, + MessageTheme? theme, + VoidCallback? onCloseBtnClick, + VoidCallback? onDurationEnd, + VoidCallback? onLinkClick, + }) { + final overlay = Overlay.of(context); + late OverlayEntry overlayEntry; + overlayEntry = OverlayEntry( + builder: (context) => TDMessage( + content: content, + visible: visible, + duration: duration, + closeBtn: closeBtn, + icon: icon, + link: link, + marquee: marquee, + offset: offset, + theme: theme, + onDurationEnd: () { + onDurationEnd?.call(); + overlayEntry.remove(); + }, + onCloseBtnClick: onCloseBtnClick, + onLinkClick: onLinkClick, + ), + ); + overlay.insert(overlayEntry); + } +} + +class _TDMessageState extends State with TickerProviderStateMixin { + bool _isVisible = true; + double _topOffset = 0; + double initTopOffset = 80; + double totalWidth = 343; + AnimationController? animationController; + bool _isAnimationRunning = false; + + @override + void initState() { + super.initState(); + _topOffset = (widget.offset?[1] ?? initTopOffset) - 30; + animationController = AnimationController( + vsync: this, + ); + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + setState(() { + _topOffset = widget.offset?[1] ?? initTopOffset; + }); + } + }); + + if (widget.duration != null && widget.duration! > 0) { + Future.delayed(Duration(milliseconds: widget.duration!), _closeMessage); + } + + if (widget.marquee != null) { + animationController = AnimationController( + vsync: this, + duration: Duration(milliseconds: widget.marquee!.speed ?? 10000), + ); + } + } + + @override + void dispose() { + animationController?.stop(); + animationController?.dispose(); + animationController = null; + super.dispose(); + } + + void _closeMessage() { + if (mounted) { + animationController?.stop(); + setState(() { + _topOffset = (widget.offset?[1] ?? initTopOffset) - 30; + _isAnimationRunning = false; + }); + Future.delayed(const Duration(milliseconds: 300), () { + if (mounted) { + setState(() { + _isVisible = false; + }); + widget.onDurationEnd?.call(); + } + }); + } + } + + void startAnimation() { + if (mounted && animationController != null && !_isAnimationRunning) { + setState(() { + _isAnimationRunning = true; + }); + if (widget.marquee!.loop == 0) { + animationController!.forward(); + } else if (widget.marquee!.loop == 1) { + animationController!.repeat(); + } + } + } + + @override + Widget build(BuildContext context) { + if (widget.visible == false) { + return const SizedBox.shrink(); + } + var _leftOffset = widget.offset?[0] ?? + (MediaQuery.of(context).size.width - totalWidth) / 2; + + Widget getText(BuildContext context) { + if (widget.marquee == null) { + return Align( + alignment: Alignment.centerLeft, + child: Text( + widget.content ?? '', + style: const TextStyle(color: Colors.black), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ); + } else { + final textPainter = TextPainter( + text: TextSpan( + text: widget.content ?? '', + style: const TextStyle(color: Colors.black)), + maxLines: 1, + textDirection: TextDirection.ltr, + )..layout(minWidth: 0, maxWidth: double.infinity); + final textWidth = textPainter.width; + + final containerWidth = calculateTextWidth(); + + final animationDuration = + Duration(milliseconds: (widget.marquee!.speed ?? 10000)); + animationController!.duration = animationDuration; + + final tween = Tween( + begin: Offset.zero, + end: Offset(-textWidth, 0), + ); + + if (widget.marquee!.delay != null && widget.marquee!.delay! > 0) { + Future.delayed( + Duration(milliseconds: widget.marquee!.delay!), startAnimation); + } else { + startAnimation(); + } + + return Align( + alignment: Alignment.center, + child: ClipRect( + child: SizedBox( + width: containerWidth, + child: AnimatedBuilder( + animation: + animationController ?? const AlwaysStoppedAnimation(0), + builder: (context, child) { + final offset = tween.evaluate( + animationController ?? const AlwaysStoppedAnimation(0)); + return OverflowBox( + minWidth: 0, + maxWidth: double.infinity, + alignment: Alignment.centerLeft, + child: Transform.translate( + offset: offset, + child: SizedBox( + child: Text( + widget.content ?? '', + style: const TextStyle(color: Colors.black), + maxLines: 1, + ), + ), + ), + ); + }, + ), + ), + )); + } + } + + Widget getIcon(BuildContext context) { + if (widget.icon is Widget) { + return widget.icon; + } else { + switch (widget.theme) { + case MessageTheme.info: + return Icon(TDIcons.error_circle_filled, + color: TDTheme.of(context).brandColor7); + case MessageTheme.success: + return Icon(TDIcons.check_circle_filled, + color: TDTheme.of(context).successColor5); + case MessageTheme.warning: + return Icon(TDIcons.error_circle_filled, + color: TDTheme.of(context).warningColor5); + case MessageTheme.error: + return Icon(TDIcons.error_circle_filled, + color: TDTheme.of(context).errorColor6); + case null: + return const SizedBox.shrink(); + } + } + } + + void clickCloseButton() { + _closeMessage(); + widget.onCloseBtnClick?.call(); + } + + Widget getCloseBtn(BuildContext context) { + if (widget.closeBtn is Widget) { + return GestureDetector( + onTap: clickCloseButton, + child: widget.closeBtn!, + ); + } else if (widget.closeBtn == true) { + return GestureDetector( + onTap: clickCloseButton, + child: const Icon( + TDIcons.close, + color: Color.fromRGBO(0, 0, 0, 0.4), + ), + ); + } else if (widget.closeBtn is String) { + return GestureDetector( + onTap: clickCloseButton, + child: Text(widget.closeBtn), + ); + } else { + return const SizedBox.shrink(); + } + } + + void clickLink() { + widget.onLinkClick?.call(); + } + + Widget getLink(BuildContext context) { + if (widget.link is MessageLink) { + return Align( + alignment: Alignment.center, + child: TDLink( + label: widget.link.name, + style: TDLinkStyle.primary, + type: TDLinkType.basic, + uri: widget.link.uri ?? Uri.parse('https://example.com'), + size: TDLinkSize.medium, + color: widget.link.color ?? TDTheme.of(context).brandColor7, + linkClick: (link) => clickLink(), + )); + } else if (widget.link is String) { + return Align( + alignment: Alignment.center, + child: GestureDetector( + onTap: clickLink, + child: Text( + widget.link ?? '', + style: TextStyle( + color: TDTheme.of(context).brandColor7, + fontSize: 14, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + )); + } else { + return const SizedBox.shrink(); + } + } + + return AnimatedPositioned( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + top: _topOffset, + left: _leftOffset, + child: _isVisible + ? Material( + color: Colors.transparent, + child: Container( + width: totalWidth, + height: 48, + padding: const EdgeInsets.fromLTRB(16, 13, 16, 13), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(6), + boxShadow: TDTheme.of(context).shadowsMiddle), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (widget.icon != false) + Padding( + padding: const EdgeInsets.only(right: 10), + child: Align( + alignment: Alignment.center, + child: SizedBox( + width: 20, + height: 22, + child: getIcon(context), + ), + ), + ), + Expanded( + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: getText(context), + flex: 3, + ), + if (widget.link != null) + Container( + margin: const EdgeInsets.only(left: 8), + width: 40, + height: 22, + child: getLink(context)), + if (widget.closeBtn != null) + Padding( + padding: const EdgeInsets.only(left: 12), + child: Align( + alignment: Alignment.center, + child: SizedBox( + width: 22, + height: 22, + child: getCloseBtn(context), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ) + : const SizedBox.shrink(), + ); + } + + double calculateTextWidth() { + double width = totalWidth - 32; + if (widget.icon != null && widget.icon != false) { + width -= 30; + } + if (widget.link != null) { + width -= 36; + } + if (widget.closeBtn != null) { + width -= 34; + } + return width; + } +} diff --git a/tdesign-component/lib/src/components/navbar/td_nav_bar.dart b/tdesign-component/lib/src/components/navbar/td_nav_bar.dart new file mode 100644 index 000000000..a52edbf88 --- /dev/null +++ b/tdesign-component/lib/src/components/navbar/td_nav_bar.dart @@ -0,0 +1,304 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +typedef TDBarItemAction = void Function(); + +class TDNavBar extends StatefulWidget implements PreferredSizeWidget { + + const TDNavBar({ + Key? key, + this.leftBarItems, + this.rightBarItems, + this.titleWidget, + this.title, + this.titleColor, + this.titleFont, + this.titleFontFamily, + this.titleFontWeight = FontWeight.w500, + this.centerTitle = true, + this.opacity = 1.0, + this.backgroundColor, + this.titleMargin = 16, + this.padding, + this.height = 44, + this.screenAdaptation = true, + this.useDefaultBack = true, + this.onBack, + this.useBorderStyle = false, + this.border, + this.belowTitleWidget, + this.boxShadow, + this.flexibleSpace, + }) : super(key: key); + + /// 左边操作项 + final List? leftBarItems; + /// 右边操作项 + final List? rightBarItems; + /// 标题控件,优先级高于title文案 + final Widget? titleWidget; + /// 标题文案 + final String? title; + /// 标题颜色 + final Color? titleColor; + /// 标题字体尺寸 + final Font? titleFont; + /// 标题字体粗细 + final FontWeight? titleFontWeight; + /// 标题字体样式 + final FontFamily? titleFontFamily; + /// 标题是否居中 + final bool centerTitle; + /// 透明度 + final double opacity; + /// 背景颜色 + final Color? backgroundColor; + /// 内部填充 + final EdgeInsetsGeometry? padding; + + /// 中间文案左右两边间距 + final double titleMargin; + /// 高度 + final double height; + + /// 是否进行屏幕适配,默认true + final bool screenAdaptation; + + /// 是否使用默认的返回 + final bool useDefaultBack; + + /// 返回事件 + final VoidCallback? onBack; + + /// 是否使用边框模式 + final bool useBorderStyle; + + /// 边框 + final TDNavBarItemBorder? border; + + /// belowTitleWidget navbar 下方的widget + final Widget? belowTitleWidget; + + /// 底部阴影 + final List? boxShadow; + + /// 固定背景 + final Widget? flexibleSpace; + + @override + State createState() => _TDNavBarState(); + + @override + Size get preferredSize => Size.fromHeight(height); +} + +class _TDNavBarState extends State { + Widget _addBorder(List items) { + var border = widget.border ?? TDNavBarItemBorder(); + var borderColor = border.color ?? TDTheme.of(context).grayColor3; + var children = []; + for (var i = 0; i < items.length; i++) { + children.add(items[i]); + if (widget.useBorderStyle && i != items.length - 1) { + children.add( + Container( + width: border.width, + height: 16.0, + color: borderColor, + ), + ); + } + } + var child = Row( + children: children, + mainAxisSize: MainAxisSize.min, + ); + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(border.radius), + border: Border.all( + color: borderColor, + width: border.width, + ), + ), + padding: border.padding ?? + EdgeInsets.symmetric(horizontal: TDTheme.of(context).spacer4), + child: child, + ); + } + + Widget get backButton { + return TDNavBarItem( + icon: Icons.chevron_left, + action: () { + widget.onBack?.call(); + Navigator.maybePop(context); + }, + ).toWidget(context); + } + + Widget _buildTitleBarItems(bool isLeft) { + var barItems = + (isLeft ? widget.leftBarItems : widget.rightBarItems) ?? []; + var children = barItems + .map( + (e) => e.toWidget(context, isLeft: isLeft), + ) + .toList(); + + return Row( + children: [ + if (isLeft && widget.useDefaultBack) + backButton, + if (children.isNotEmpty) + widget.useBorderStyle + ? _addBorder(children) + : Row( + children: children, + mainAxisSize: MainAxisSize.min, + ), + ], + mainAxisSize: MainAxisSize.min, + ); + } + + TextStyle _getTitleStyle(BuildContext context) { + var titleColor = widget.titleColor ?? TDTheme.of(context).fontGyColor1; + + var titleFont = widget.titleFont ?? TDTheme.of(context).fontBodyLarge; + + return widget.titleFontFamily == null + ? TextStyle( + fontSize: titleFont?.size, + color: titleColor, + fontWeight: widget.titleFontWeight ?? FontWeight.w500, + decoration: TextDecoration.none, + ) + : TextStyle( + fontSize: titleFont?.size, + color: titleColor, + fontWeight: widget.titleFontWeight ?? FontWeight.w500, + decoration: TextDecoration.none, + fontFamily: widget.titleFontFamily!.fontFamily, + package: 'tdesign_flutter'); + } + + Widget _getTitleWidget(BuildContext context) { + return widget.titleWidget ?? + Text( + widget.title ?? '', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: _getTitleStyle(context), + ); + } + + Widget _getNavbarChild(){ + final Widget toolbar = NavigationToolbar( + leading: _buildTitleBarItems(true), + middle: _getTitleWidget(context), + trailing: _buildTitleBarItems(false), + middleSpacing: widget.titleMargin, + centerMiddle: widget.centerTitle, + ); + if (widget.belowTitleWidget == null) { + return toolbar; + } + var children = [ + Expanded(child: toolbar) + ]; + children.add(widget.belowTitleWidget as Widget); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ); + } + + @override + Widget build(BuildContext context) { + var bcc = widget.backgroundColor ?? TDTheme.of(context).whiteColor1; + if (bcc != Colors.transparent) { + bcc = bcc.withOpacity(widget.opacity); + } + + var paddingTop = widget.screenAdaptation ? MediaQuery.of(context).padding.top : 0.0; + var padding = widget.padding ?? + EdgeInsets.symmetric( + horizontal: TDTheme.of(context).spacer16, + vertical: TDTheme.of(context).spacer4, + ); + Widget appBar = Container( + height: widget.height + paddingTop, + padding: padding.add(EdgeInsets.only(top: paddingTop)), + decoration: BoxDecoration( + color: bcc, + boxShadow: widget.boxShadow, + ), + child: _getNavbarChild() + ); + if (widget.flexibleSpace != null) { + appBar = Stack( + fit: StackFit.passthrough, + children: [ + widget.flexibleSpace!, + appBar, + ], + ); + } + + return appBar; + } +} + +class TDNavBarItem { + /// 图标 + IconData? icon; + /// 图标颜色 + Color? iconColor; + /// 操作回调 + TDBarItemAction? action; + /// 图标尺寸 + double? iconSize; + /// 内部填充 + EdgeInsetsGeometry? padding; + /// 图标组件,优先级高与icon + Widget? iconWidget; + + TDNavBarItem({ + this.icon, + this.iconColor, + this.action, + this.iconSize = 24.0, + this.padding, + this.iconWidget, + }); + + Widget toWidget(BuildContext context,{bool isLeft = true}) => GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: action, + child: Padding( + padding: + padding ?? (isLeft ? EdgeInsets.only(right: TDTheme.of(context).spacer8) : EdgeInsets.only(left: TDTheme.of(context).spacer8)), + child: iconWidget ?? + Icon( + icon, + size: iconSize, + color: iconColor, + ), + ), + ); +} + +class TDNavBarItemBorder { + double width; + double radius; + Color? color; + EdgeInsetsGeometry? padding; + + TDNavBarItemBorder({ + this.width = 1.0, + this.radius = 22.0, + this.color, + this.padding, + }); +} diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart new file mode 100644 index 000000000..9299a7af3 --- /dev/null +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar.dart @@ -0,0 +1,422 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +import '../../../tdesign_flutter.dart'; + +class TDNoticeBar extends StatefulWidget { + const TDNoticeBar({ + super.key, + this.context, + this.style, + this.left, + this.right, + this.speed = 50, + this.interval = 3000, + this.marquee = false, + this.direction = Axis.horizontal, + this.theme = TDNoticeBarTheme.info, + this.prefixIcon, + this.suffixIcon, + this.onTap, + this.height = 22, + this.maxLines = 1, + }); + + /// 文本内容 + final dynamic context; + + /// 公告栏样式 + final TDNoticeBarStyle? style; + + /// 左侧内容(自定义左侧内容,优先级高于prefixIcon) + final Widget? left; + + /// 右侧内容(自定义右侧内容,优先级高于suffixIcon) + final Widget? right; + + /// 跑马灯效果 + final bool? marquee; + + /// 滚动速度 + final double? speed; + + /// 步进滚动间隔时间(毫秒) + final int? interval; + + /// 滚动方向 + final Axis? direction; + + /// 主题 + final TDNoticeBarTheme? theme; + + /// 左侧图标 + final IconData? prefixIcon; + + /// 右侧图标 + final IconData? suffixIcon; + + /// 点击事件 + final ValueChanged? onTap; + + /// 文字高度 (当使用prefixIcon或suffixIcon时,icon大小值等于该属性) + final double height; + + /// 文本行数(仅静态有效) + final int? maxLines; + + @override + State createState() => _TDNoticeBarState(); +} + +class _TDNoticeBarState extends State { + ScrollController? _scrollController; + Timer? _timer; + Size? _size; + TDNoticeBarStyle? _style; + Color? _backgroundColor; + Widget? _left; + Widget? _right; + final GlobalKey _key = GlobalKey(); + final GlobalKey _contextKey = GlobalKey(); + + @override + void initState() { + super.initState(); + if (widget.speed! < 0) { + throw Exception('speed must not be less than 0'); + } + if (widget.interval! <= 0) { + throw Exception('interval must not be less than 0'); + } + _scrollController = ScrollController(); + WidgetsBinding.instance.addPostFrameCallback((time) { + if (widget.marquee == true) { + _startTimer(); + } + }); + } + + @override + void dispose() { + super.dispose(); + _timer?.cancel(); + _scrollController?.dispose(); + } + + void _init() { + if (widget.style != null) { + _style = widget.style; + } else { + _style = TDNoticeBarStyle.generateTheme(context, theme: widget.theme); + } + _backgroundColor = _style!.backgroundColor; + _setLeftWidget(); + _setRightWidget(); + } + + void _startTimer() { + if (widget.direction == Axis.horizontal) { + _scroll(); + } else if (widget.direction == Axis.vertical) { + _step(); + } + } + + void _scroll() { + var scrollDistance = + _getContextWidth() + (_size!.width - _style!.getPadding.horizontal); + var remainder = scrollDistance % widget.speed!; + _scrollController!.jumpTo(0); + var offset = 0.0 + widget.speed!; + _scrollController!.animateTo(offset, + duration: const Duration(seconds: 1), curve: Curves.linear); + _timer = Timer.periodic(const Duration(seconds: 1), (timer) async { + if (offset < scrollDistance - remainder) { + offset += widget.speed!; + await _scrollController!.animateTo(offset, + duration: const Duration(seconds: 1), curve: Curves.linear); + } else { + // 剩余距离小于50 先滚动这部分 然后滚动剩余部分 + // 剩余距离滚动所需时间 + var time = (remainder / widget.speed! * 1000).round(); + // 滚动最后一部分(触底) + await _scrollController!.animateTo(scrollDistance, + duration: Duration(milliseconds: time), curve: Curves.linear); + // 回到顶部(衔接) + _scrollController!.jumpTo(0); + // 修改起始位置 + offset = widget.speed! - remainder; + // 计算新起点最后阶段滚动距离 + remainder = (scrollDistance - offset) % widget.speed!; + // 滚动至新起点(弥补触底speed滚动长度) + await _scrollController!.animateTo(offset, + duration: Duration(milliseconds: 1000 - time), + curve: Curves.linear); + } + }); + } + + void _step() { + var step = 0; + var offset = 0.0; + _timer = Timer.periodic(Duration(milliseconds: widget.interval!), (timer) { + var time = (widget.height / widget.speed! * 1000).round(); + if (step >= widget.context.length) { + step = 0; + offset = 0; + _scrollController!.jumpTo(0); + } + step++; + // 固定滚动行高(22) + offset += widget.height; + _scrollController!.animateTo(offset, + duration: Duration(milliseconds: time), curve: Curves.linear); + }); + } + + /// 获取文本内容尺寸消息 + Size _getFontSize() { + var text = widget.context; + if (widget.context is List) { + text = widget.context[0]; + } + final textPainter = TextPainter( + text: TextSpan( + text: text, + style: _style!.getTextStyle, + ), + locale: Localizations.localeOf(context), + textDirection: TextDirection.ltr, + maxLines: widget.marquee! ? 1 : widget.maxLines, + )..layout(maxWidth: _size!.width); + return textPainter.size; + } + + /// 设置左侧内容 + void _setLeftWidget() { + if (widget.prefixIcon != null) { + _left = Icon( + widget.prefixIcon, + color: _style!.leftIconColor, + size: widget.height, + ); + } + if (widget.left != null) { + _left = widget.left; + } + } + + /// 设置右侧内容 + void _setRightWidget() { + if (widget.suffixIcon != null) { + _right = Icon( + widget.suffixIcon, + color: _style!.rightIconColor, + size: widget.height, + ); + } + if (widget.right != null) { + _right = widget.right; + } + } + + /// 获取文本内容宽度 + double _getContextWidth() { + var contextWidth = + _key.currentContext?.findRenderObject()?.paintBounds.size.width ?? 0; + if (contextWidth == 0) { + contextWidth = _getFontSize().width; + } + return contextWidth; + } + + /// 获取滚动区域宽度 + double _getEmptyWidth() { + return _contextKey.currentContext + ?.findRenderObject() + ?.paintBounds + .size + .width ?? + (_size!.width - _style!.getPadding.horizontal); + } + + /// 获取文字高度 + double _getTextHeight() { + return _getFontSize().height; + } + + /// 内容区域 + Widget _contextWidget() { + var valid = false; + Widget? textWidget; + if (widget.context is String) { + valid = true; + textWidget = SizedBox( + height: _getTextHeight(), + child: Align( + alignment: Alignment.centerLeft, + child: SizedBox( + height: _getTextHeight(), + child: TDText( + widget.context, + style: _style?.getTextStyle, + maxLines: widget.marquee! ? 1 : widget.maxLines, + forceVerticalCenter: true, + ), + ), + ), + ); + } + if (widget.context is List) { + valid = true; + textWidget = SizedBox( + height: _getTextHeight(), + child: Align( + alignment: Alignment.centerLeft, + child: SizedBox( + height: _getTextHeight(), + child: TDText( + widget.context[0], + style: _style?.getTextStyle, + maxLines: 1, + forceVerticalCenter: true, + ), + ), + ), + ); + } + if (!valid) { + throw Exception('context must be String or List'); + } + if (widget.marquee == false) { + return textWidget!; + } + Widget? child; + switch (widget.direction) { + case Axis.horizontal: + child = SingleChildScrollView( + controller: _scrollController, + scrollDirection: Axis.horizontal, + physics: const NeverScrollableScrollPhysics(), + child: Row( + children: [ + SizedBox( + key: _key, + height: _getTextHeight(), + child: textWidget, + ), + SizedBox(width: _getEmptyWidth()), + SizedBox( + width: _getEmptyWidth() > _getContextWidth() + ? _getEmptyWidth() + : _getContextWidth(), + height: _getTextHeight(), + child: textWidget, + ) + ], + ), + ); + break; + case Axis.vertical: + var contexts = widget.context as List; + child = SizedBox( + height: widget.height, + child: SingleChildScrollView( + controller: _scrollController, + scrollDirection: Axis.vertical, + // physics: const NeverScrollableScrollPhysics(), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (int i = 0; i < contexts.length; i++) + SizedBox( + height: widget.height, + child: Align( + alignment: Alignment.centerLeft, + child: TDText( + contexts[i], + style: _style!.getTextStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ), + SizedBox( + key: _key, + height: widget.height, + child: Align( + alignment: Alignment.centerLeft, + child: TDText( + contexts[0], + style: _style?.getTextStyle, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ]), + ), + ); + break; + default: + child = textWidget; + break; + } + return child!; + } + + void _onTap(trigger) { + if (widget.onTap != null) { + widget.onTap!(trigger); + } + } + + @override + Widget build(BuildContext context) { + // 初始化样式及左右widget + _init(); + _size = MediaQuery.of(context).size; + return Container( + padding: _style!.getPadding, + decoration: BoxDecoration( + color: _backgroundColor, + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Visibility( + visible: _left != null, + child: GestureDetector( + onTap: () => _onTap('prefix-icon'), + child: Container( + margin: const EdgeInsets.only(right: 8), + child: _left, + ), + ), + ), + Expanded( + key: _contextKey, + child: GestureDetector( + onTap: () => _onTap('context'), + child: _contextWidget(), + ), + ), + Visibility( + visible: _right != null, + child: GestureDetector( + onTap: () => _onTap('suffix-icon'), + child: _right, + ), + ), + ], + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart b/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart new file mode 100644 index 000000000..b08c4e184 --- /dev/null +++ b/tdesign-component/lib/src/components/notice_bar/td_notice_bar_style.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; + +import '../../theme/td_colors.dart'; +import '../../theme/td_theme.dart'; +import 'td_notice_bar.dart'; + +/// 公告栏类型 +enum TDNoticeBarType { + /// 静止(默认) + none, + + /// 滚动 + scroll, + + /// 步进 + step +} + +/// 公告栏主题 +enum TDNoticeBarTheme { + /// 默认 + info, + + /// 成功 + success, + + /// 警告 + warning, + + /// 错误 + error +} + +/// 公告栏样式 +class TDNoticeBarStyle { + TDNoticeBarStyle( + {this.context, + this.backgroundColor, + this.textStyle, + this.leftIconColor, + this.rightIconColor, + this.padding}); + + /// 上下文 + BuildContext? context; + + /// 公告栏背景色 + Color? backgroundColor; + + /// 公告栏左侧图标颜色 + Color? leftIconColor; + + /// 公告栏右侧图标颜色 + Color? rightIconColor; + + /// 公告栏内边距 + EdgeInsetsGeometry? padding; + + /// 公告栏内容样式 + TextStyle? textStyle; + + /// 公告栏内边距,用于获取默认值 + EdgeInsetsGeometry get getPadding => + padding ?? + const EdgeInsets.only(top: 13, bottom: 13, left: 16, right: 12); + + /// 公告栏内容样式,用于获取默认值 + TextStyle get getTextStyle => + textStyle ?? + TextStyle( + color: TDTheme.of(context).fontGyColor1, + fontSize: 14, + height: 1, + fontWeight: FontWeight.normal, + fontStyle: FontStyle.normal, + ); + + /// 根据主题生成样式 + TDNoticeBarStyle.generateTheme(BuildContext context, + {TDNoticeBarTheme? theme = TDNoticeBarTheme.info}) { + rightIconColor = TDTheme.of(context).grayColor7; + switch (theme) { + case TDNoticeBarTheme.warning: + leftIconColor = TDTheme.of(context).warningNormalColor; + backgroundColor = TDTheme.of(context).warningLightColor; + break; + case TDNoticeBarTheme.error: + leftIconColor = TDTheme.of(context).errorNormalColor; + backgroundColor = TDTheme.of(context).errorLightColor; + break; + case TDNoticeBarTheme.success: + leftIconColor = TDTheme.of(context).successNormalColor; + backgroundColor = TDTheme.of(context).successLightColor; + break; + default: + leftIconColor = TDTheme.of(context).brandNormalColor; + backgroundColor = TDTheme.of(context).brandLightColor; + break; + } + } +} diff --git a/tdesign-component/lib/src/components/picker/no_wave_behavior.dart b/tdesign-component/lib/src/components/picker/no_wave_behavior.dart new file mode 100644 index 000000000..1a68c65e1 --- /dev/null +++ b/tdesign-component/lib/src/components/picker/no_wave_behavior.dart @@ -0,0 +1,32 @@ +import 'dart:io'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import '../../util/platform_util.dart'; + +/// 去掉ListView上下滑动的波纹 +class NoWaveBehavior extends ScrollBehavior { + @override + Widget buildOverscrollIndicator( + BuildContext context, Widget child, ScrollableDetails details) { + if (PlatformUtil.isAndroid || PlatformUtil.isFuchsia) { + return child; + } else { + return super.buildOverscrollIndicator(context, child, details); + } + } + + // 增加mouse拖拽 + @override + Set get dragDevices => { + PointerDeviceKind.touch, + PointerDeviceKind.stylus, + PointerDeviceKind.invertedStylus, + PointerDeviceKind.trackpad, + // The VoiceAccess sends pointer events with unknown type when scrolling + // scrollables. + PointerDeviceKind.unknown, + PointerDeviceKind.mouse, + }; + +} diff --git a/tdesign-component/lib/src/components/picker/td_date_picker.dart b/tdesign-component/lib/src/components/picker/td_date_picker.dart new file mode 100644 index 000000000..6030b8987 --- /dev/null +++ b/tdesign-component/lib/src/components/picker/td_date_picker.dart @@ -0,0 +1,819 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; +import 'no_wave_behavior.dart'; + +typedef DatePickerCallback = void Function(Map selected); +enum DateTypeKey { year, month, weekDay, day, hour, minute, second } + +/// 时间选择器 +class TDDatePicker extends StatefulWidget { + const TDDatePicker( + {required this.title, + required this.onConfirm, + this.rightText, + this.leftText, + this.onCancel, + this.backgroundColor, + this.titleDividerColor, + this.topRadius, + this.titleHeight, + this.padding, + this.leftPadding, + this.rightPadding, + this.leftTextStyle, + this.rightTextStyle, + this.centerTextStyle, + this.customSelectWidget, + this.itemDistanceCalculator, + required this.model, + this.showTitle = true, + this.pickerHeight = 200, + required this.pickerItemCount, + this.isTimeUnit, + this.onSelectedItemChanged, + this.itemBuilder, + Key? key}) + : super(key: key); + + /// 选择器标题 + final String title; + + /// 右侧按钮文案 + final String? rightText; + + /// 左侧按钮文案 + final String? leftText; + + /// 选择器确认按钮回调 + final DatePickerCallback? onConfirm; + + /// 选择器取消按钮回调 + final DatePickerCallback? onCancel; + + /// 背景颜色 + final Color? backgroundColor; + + /// 标题分割线颜色 + final Color? titleDividerColor; + + /// 顶部圆角 + final double? topRadius; + + /// 标题高度 + final double? titleHeight; + + /// 左边填充 + final double? leftPadding; + + /// 右边填充 + final double? rightPadding; + + /// 根据距离计算字体颜色、透明度、粗细 + final ItemDistanceCalculator? itemDistanceCalculator; + + /// 选择器List的视窗高度,默认200 + final double pickerHeight; + + /// 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 + final int pickerItemCount; + + /// 自定义选择框样式 + final Widget? customSelectWidget; + + /// 自定义左侧文案样式 + final TextStyle? leftTextStyle; + + /// 自定义右侧文案样式 + final TextStyle? rightTextStyle; + + /// 自定义中间文案样式 + final TextStyle? centerTextStyle; + + /// 适配padding + final EdgeInsets? padding; + + /// 是否展示标题 + final bool showTitle; + + /// 数据模型 + final DatePickerModel model; + + /// 是否时间显示 + final bool? isTimeUnit; + + /// 选择器选中项改变回调 + final void Function(int wheelIndex,int index)? onSelectedItemChanged; + + /// 自定义item构建 + final ItemBuilderType? itemBuilder; + + @override + State createState() => _TDDatePickerState(); +} + +class _TDDatePickerState extends State { + double pickerHeight = 0; + static const _pickerTitleHeight = 56.0; + + @override + void initState() { + super.initState(); + pickerHeight = widget.pickerHeight; + } + + @override + void dispose() { + widget.model.removeListener(); + super.dispose(); + } + + bool useAll() { + if (widget.model.useYear && + widget.model.useMonth && + widget.model.useDay && + widget.model.useHour && + widget.model.useMinute && + widget.model.useSecond) { + return true; + } + return false; + } + + selectListItem(String ev) { + var selected = { + 'year': widget.model.useYear + ? widget.model.yearFixedExtentScrollController.selectedItem + widget.model.data[0][0] + : -1, + 'month': widget.model.useMonth + ? widget.model.monthFixedExtentScrollController.selectedItem + widget.model.data[1][0] + : -1, + 'day': + widget.model.useDay ? widget.model.dayFixedExtentScrollController.selectedItem + widget.model.data[2][0] : -1, + 'weekDay': widget.model.useWeekDay + ? widget.model.weekDayFixedExtentScrollController.selectedItem + widget.model.data[3][0] + : -1, + 'hour': widget.model.useHour + ? selectItemValue(widget.model.data[4], widget.model.hourFixedExtentScrollController.selectedItem) + : -1, + 'minute': widget.model.useMinute + ? selectItemValue(widget.model.data[5], widget.model.minuteFixedExtentScrollController.selectedItem) + : -1, + 'second': widget.model.useSecond + ? selectItemValue(widget.model.data[6], widget.model.secondFixedExtentScrollController.selectedItem) + : -1, + }; + if (ev == 'onCancel' && widget.onCancel != null) { + widget.onCancel!(selected); + } else if (ev == 'onConfirm' && widget.onConfirm != null) { + widget.onConfirm!(selected); + } else { + Navigator.of(context).pop(); + } + } + + int selectItemValue(List items, int itemIndex) { + ///选择列表索引对应的项的值 + return items[itemIndex]; + } + + @override + Widget build(BuildContext context) { + var maxWidth = MediaQuery.of(context).size.width; + return Container( + width: maxWidth, + padding: widget.padding ?? EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + decoration: BoxDecoration( + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(widget.topRadius ?? TDTheme.of(context).radiusExtraLarge), + topRight: Radius.circular(widget.topRadius ?? TDTheme.of(context).radiusExtraLarge), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Visibility( + child: buildTitle(context), + visible: widget.showTitle == true, + ), + SizedBox( + height: pickerHeight, + child: Stack( + alignment: Alignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: widget.customSelectWidget ?? + Container( + height: 40, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: const BorderRadius.all(Radius.circular(6))), + ), + ), + Container( + height: pickerHeight, + width: maxWidth, + padding: const EdgeInsets.only(left: 32, right: 32), + child: Row( + children: [ + widget.model.useYear + ? useAll() + ? SizedBox( + child: buildList(context, 0), + width: 64, + ) + : Expanded(child: buildList(context, 0)) + : Container(), + widget.model.useMonth ? Expanded(child: buildList(context, 1)) : Container(), + widget.model.useDay ? Expanded(child: buildList(context, 2)) : Container(), + widget.model.useWeekDay ? Expanded(child: buildList(context, 3)) : Container(), + widget.model.useHour ? Expanded(child: buildList(context, 4)) : Container(), + widget.model.useMinute ? Expanded(child: buildList(context, 5)) : Container(), + widget.model.useSecond ? Expanded(child: buildList(context, 6)) : Container(), + ], + )), + // 蒙层 + Positioned( + top: 0, + child: IgnorePointer( + ignoring: true, + child: Container( + height: _pickerTitleHeight, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + gradient: LinearGradient(begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ + TDTheme.of(context).whiteColor1, + TDTheme.of(context).whiteColor1.withOpacity(0) + ])), + ), + ), + ), + Positioned( + bottom: 0, + child: IgnorePointer( + ignoring: true, + child: Container( + height: _pickerTitleHeight, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + gradient: LinearGradient(begin: Alignment.bottomCenter, end: Alignment.topCenter, colors: [ + TDTheme.of(context).whiteColor1, + TDTheme.of(context).whiteColor1.withOpacity(0) + ])), + ), + ), + ) + ], + ), + ) + ], + ), + ); + } + + Widget buildList(context, int whichLine) { + /// whichLine参数表示这个列表表示的是年,还是月还是日...... + var maxWidth = MediaQuery.of(context).size.width; + return MediaQuery.removePadding( + context: context, + removeTop: true, + child: ScrollConfiguration( + behavior: NoWaveBehavior(), + child: ListWheelScrollView.useDelegate( + itemExtent: pickerHeight / widget.pickerItemCount, + diameterRatio: 100, + controller: widget.model.controllers[whichLine], + physics: whichLine == 3 ? const NeverScrollableScrollPhysics() : const FixedExtentScrollPhysics(), + onSelectedItemChanged: (index) { + if (whichLine == 0 || + whichLine == 1 || + whichLine == 2 || + whichLine == 4 || + whichLine == 5 || + whichLine == 6) { + // 年月的改变会引起日的改变, 年的改变会引起月的改变 + setState(() { + switch (whichLine) { + case 0: + widget.model.refreshMonthDataAndController(); + widget.model.refreshDayDataAndController(); + if (widget.model.useWeekDay) { + widget.model.refreshWeekDayDataAndController(); + } + break; + case 1: + widget.model.refreshDayDataAndController(); + if (widget.model.useWeekDay) { + widget.model.refreshWeekDayDataAndController(); + } + break; + case 2: + if (widget.model.useWeekDay) { + widget.model.refreshWeekDayDataAndController(); + } + break; + } + if (useAll()) { + widget.model.refreshTimeDataInitialAndController(whichLine); + } + + /// 使用动态高度,强制列表组件的state刷新,以展现更新的数据,详见下方链接 + /// FIX:https://github.com/flutter/flutter/issues/22999 + pickerHeight = pickerHeight - Random().nextDouble() / 100000000; + }); + } + widget.onSelectedItemChanged?.call(whichLine,index); + }, + childDelegate: ListWheelChildBuilderDelegate( + childCount: widget.model.data[whichLine].length, + builder: (context, index) { + return Container( + alignment: Alignment.center, + height: pickerHeight / widget.pickerItemCount, + width: maxWidth, + child: TDItemWidget( + colIndex: whichLine, + index: index, + itemHeight: pickerHeight / widget.pickerItemCount, + content: whichLine == 3 + ? weekUnitMap(widget.model.data[whichLine][index] - 1) + : widget.model.data[whichLine][index].toString() + timeUnitMap(widget.model.mapping[whichLine]), + fixedExtentScrollController: widget.model.controllers[whichLine], + itemDistanceCalculator: widget.itemDistanceCalculator, + itemBuilder: widget.itemBuilder, + )); + })), + )); + } + + Widget buildTitle(BuildContext context) { + return Container( + padding: EdgeInsets.only(left: widget.leftPadding ?? 16, right: widget.rightPadding ?? 16), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1, + color: widget.titleDividerColor ?? Colors.transparent, + ), + )), + + /// 减去分割线的空间 + height: getTitleHeight() - 0.5, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + /// 左边按钮 + GestureDetector( + onTap: () { + selectListItem('onCancel'); + }, + behavior: HitTestBehavior.opaque, + child: TDText(widget.leftText ?? context.resource.cancel, + style: widget.leftTextStyle ?? + TextStyle( + fontSize: TDTheme.of(context).fontBodyLarge!.size, color: TDTheme.of(context).fontGyColor2))), + + /// 中间title + Expanded( + child: Center( + child: TDText( + widget.title, + style: widget.centerTextStyle ?? + TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + color: TDTheme.of(context).fontGyColor1, + ), + ), + ), + ), + + /// 右边按钮 + GestureDetector( + onTap: () { + selectListItem('onConfirm'); + }, + behavior: HitTestBehavior.opaque, + child: TDText( + widget.rightText ?? context.resource.confirm, + style: widget.rightTextStyle ?? + TextStyle( + fontSize: TDTheme.of(context).fontBodyLarge!.size, color: TDTheme.of(context).brandNormalColor), + ), + ), + ], + ), + ); + } + timeUnitMap(String name){ + if(widget.isTimeUnit!=null&&widget.isTimeUnit==true){ + var times={ + '年':context.resource.yearLabel, + '月':context.resource.monthLabel, + '日': context.resource.dateLabel, + '周':context.resource.weeksLabel, + '时':context.resource.hours, + '分':context.resource.minutes, + '秒':context.resource.seconds + }; + return times[name]; + }else{ + return ''; + } + } + + weekUnitMap(int index){ + if(index < 0 || index > 6){ + return ''; + } + return [ + context.resource.monday, + context.resource.tuesday, + context.resource.wednesday, + context.resource.thursday, + context.resource.friday, + context.resource.saturday, + context.resource.sunday, + ][index]; + } + + + double getTitleHeight() => widget.titleHeight ?? _pickerTitleHeight; +} + +// 时间选择器的数据类 +class DatePickerModel { + bool useYear; + bool useMonth; + bool useDay; + bool useWeekDay; + bool useHour; + bool useMinute; + bool useSecond; + List dateStart; + List dateEnd; + List? dateInitial; + List Function(DateTypeKey key, List nums)? filterItems; + final mapping = ['年', '月', '日', '周', '时', '分', '秒']; + final weekMap= ['一', '二', '三', '四', '五', '六', '日']; + + late DateTime initialTime; + + /// 这四项随滑动而更新,注意初始化 + late int yearIndex; + late int monthIndex; + late int dayIndex; + late int weekDayIndex; + late int hourIndex; + late int minuteIndex; + late int secondIndex; + late List> data = [ + [], + [], + [], + [], + [], + [], + [], + ]; + late var controllers; + late FixedExtentScrollController yearFixedExtentScrollController; + late FixedExtentScrollController monthFixedExtentScrollController; + late FixedExtentScrollController dayFixedExtentScrollController; + late FixedExtentScrollController weekDayFixedExtentScrollController; + late FixedExtentScrollController hourFixedExtentScrollController; + late FixedExtentScrollController minuteFixedExtentScrollController; + late FixedExtentScrollController secondFixedExtentScrollController; + + DatePickerModel( + {required this.useYear, + required this.useMonth, + required this.useDay, + required this.useHour, + required this.useMinute, + required this.useWeekDay, + required this.useSecond, + required this.dateStart, + required this.dateEnd, + this.dateInitial, + this.filterItems, +}) { + assert(!useWeekDay || (!useSecond && !useMinute && !useHour), 'WeekDay can only used with Year, Month and Day!'); + setInitialTime(); + setInitialYearData(); + setInitialMonthData(); + setInitialDayData(); + setInitialWeekDayData(); + setInitialTimeData(); + setControllers(); + addListener(); + } + + void setInitialTime() { + dateStart = List.generate(6, (index) => index < dateStart.length ? dateStart[index] : 0); + var startTime = DateTime(dateStart[0], dateStart[1], dateStart[2], dateStart[3], dateStart[4], dateStart[5]); + dateEnd = List.generate(6, (index) => index < dateEnd.length ? dateEnd[index] : 0); + var endTime = DateTime( + dateEnd[0], + dateEnd[1], + dateEnd[2], + dateEnd[3], + dateEnd[4], + dateEnd[5], + ); + if (dateInitial != null) { + var initList = List.generate(6, (index) => index < dateInitial!.length ? dateInitial![index] : 0); + initialTime = DateTime(initList[0], initList[1], initList[2], initList[3], initList[4], initList[5]); + if (initialTime.isBefore(startTime)) { + initialTime = startTime; + } else if (initialTime.isAfter(endTime)) { + initialTime = endTime; + } + return; + } + + var now = DateTime.now(); + if (now.isBefore(startTime)) { + initialTime = startTime.copyWith(); + } else if (now.isAfter(endTime)) { + initialTime = startTime.copyWith(); + } else { + initialTime = now; + } + } + + void setInitialYearData() { + var years = List.generate(dateEnd[0] - dateStart[0] + 1, (index) => index + dateStart[0]); + data[0] = useYear && filterItems != null ? filterItems!(DateTypeKey.year, years) : years; + } + + void setInitialMonthData() { + late List month; + if (dateEnd[0] == dateStart[0]) { + month = List.generate(dateEnd[1] - dateStart[1] + 1, (index) => index + dateStart[1]); + } else if (initialTime.year == dateStart[0]) { + month = List.generate(12 - dateStart[1] + 1, (index) => index + dateStart[1]); + } else if (initialTime.year == dateEnd[0]) { + month = List.generate(dateEnd[1], (index) => index + 1); + } else { + month = List.generate(12, (index) => index + 1); + } + data[1] = useMonth && filterItems != null ? filterItems!(DateTypeKey.month, month) : month; + } + + void setInitialDayData() { + late List day; + if (dateEnd[0] == dateStart[0] && dateEnd[1] == dateStart[1]) { + day = List.generate(dateEnd[2] - dateStart[2] + 1, (index) => index + dateStart[2]); + } else if (initialTime.year == dateStart[0] && initialTime.month == dateStart[1]) { + day = List.generate( + DateTime(initialTime.year, initialTime.month + 1, 0).day - dateStart[2] + 1, (index) => index + dateStart[2]); + } else if (initialTime.year == dateEnd[0] && initialTime.month == dateEnd[1]) { + day = List.generate(dateEnd[2], (index) => index + 1); + } else { + day = List.generate(DateTime(initialTime.year, initialTime.month + 1, 0).day, (index) => index + 1); + } + data[2] = useDay && filterItems != null ? filterItems!(DateTypeKey.day, day) : day; + } + + void setInitialWeekDayData() { + var weekDay = [1, 2, 3, 4, 5, 6, 7]; + data[3] = useWeekDay && filterItems != null ? filterItems!(DateTypeKey.weekDay, weekDay) : weekDay; + } + + void setInitialTimeData() { + var hour = List.generate(24, (index) => index); + var minute = List.generate(60, (index) => index); + var second = List.generate(60, (index) => index); + if (dateStart.length > 3) { + if(!useYear&&!useMonth&&!useDay&&dateEnd[0] == dateStart[0] && dateEnd[1] == dateStart[1]&& dateEnd[2] == dateStart[2]){ + hour = List.generate(max(0, dateEnd[3] - dateStart[3] + 1), (i) => i + dateStart[3]); + minute = List.generate(max(0, dateEnd[4] - dateStart[4] + 1), (i) => i + dateStart[4]); + second = List.generate(max(0, dateEnd[5] - dateStart[5] + 1), (i) => i + dateStart[5]); + + data[4] = useHour && filterItems != null ? filterItems!(DateTypeKey.hour, hour) : hour; + data[5] = useMinute && filterItems != null ? filterItems!(DateTypeKey.minute, minute) : minute; + data[6] = useSecond && filterItems != null ? filterItems!(DateTypeKey.second, second) : second; + return; + } + if (initialTime.hour >= dateStart[3]) { + hour = List.generate(24 - dateStart[3], (index) => index + dateStart[3]); + } + if (initialTime.minute >= dateStart[4]) { + minute = List.generate(60 - dateStart[4], (index) => index + dateStart[4]); + } + if (initialTime.second >= dateStart[5]) { + second = List.generate(60 - dateStart[5], (index) => index + dateStart[5]); + } + } + data[4] = useHour && filterItems != null ? filterItems!(DateTypeKey.hour, hour) : hour; + data[5] = useMinute && filterItems != null ? filterItems!(DateTypeKey.minute, minute) : minute; + data[6] = useSecond && filterItems != null ? filterItems!(DateTypeKey.second, second) : second; + } + + void setControllers() { + /// 初始化Index + yearIndex = initialTime.year - data[0][0]; + monthIndex = initialTime.month - data[1][0]; + dayIndex = initialTime.day - data[2][0]; + weekDayIndex = initialTime.weekday - 1; + hourIndex = initialTime.hour - data[4][0]; + minuteIndex = initialTime.minute - data[5][0]; + secondIndex = initialTime.second - data[6][0]; + controllers = [ + FixedExtentScrollController(initialItem: yearIndex), + FixedExtentScrollController(initialItem: monthIndex), + FixedExtentScrollController(initialItem: dayIndex), + FixedExtentScrollController(initialItem: weekDayIndex), + FixedExtentScrollController(initialItem: hourIndex), + FixedExtentScrollController(initialItem: minuteIndex), + FixedExtentScrollController(initialItem: secondIndex) + ]; + yearFixedExtentScrollController = controllers[0]; + monthFixedExtentScrollController = controllers[1]; + dayFixedExtentScrollController = controllers[2]; + weekDayFixedExtentScrollController = controllers[3]; + hourFixedExtentScrollController = controllers[4]; + minuteFixedExtentScrollController = controllers[5]; + secondFixedExtentScrollController = controllers[6]; + } + + void _yearListener() { + yearIndex = yearFixedExtentScrollController.selectedItem; + } + + void _monthListener() { + monthIndex = monthFixedExtentScrollController.selectedItem; + } + + void _dayListener() { + dayIndex = dayFixedExtentScrollController.selectedItem; + } + + void _weekDayListener() { + weekDayIndex = weekDayFixedExtentScrollController.selectedItem; + } + + void _hourDayListener() { + hourIndex = hourFixedExtentScrollController.selectedItem; + } + + void _minuteDayListener() { + minuteIndex = minuteFixedExtentScrollController.selectedItem; + } + + void _secondDayListener() { + secondIndex = secondFixedExtentScrollController.selectedItem; + } + + void addListener() { + /// 给年月日加上监控 + yearFixedExtentScrollController.addListener(_yearListener); + monthFixedExtentScrollController.addListener(_monthListener); + dayFixedExtentScrollController.addListener(_dayListener); + weekDayFixedExtentScrollController.addListener(_weekDayListener); + hourFixedExtentScrollController.addListener(_hourDayListener); + minuteFixedExtentScrollController.addListener(_minuteDayListener); + secondFixedExtentScrollController.addListener(_secondDayListener); + } + + void removeListener() { + /// 移除年月日的监控 + yearFixedExtentScrollController.removeListener(_yearListener); + monthFixedExtentScrollController.removeListener(_monthListener); + dayFixedExtentScrollController.removeListener(_dayListener); + weekDayFixedExtentScrollController.removeListener(_weekDayListener); + hourFixedExtentScrollController.removeListener(_hourDayListener); + minuteFixedExtentScrollController.removeListener(_minuteDayListener); + secondFixedExtentScrollController.removeListener(_secondDayListener); + } + + void refreshMonthDataAndController() { + var selectedYear = yearIndex + data[0][0]; + late List month; + if (dateEnd[0] == dateStart[0]) { + month = List.generate(dateEnd[1] - dateStart[1] + 1, (index) => index + dateStart[1]); + } else if (selectedYear == dateStart[0]) { + month = List.generate(12 - dateStart[1] + 1, (index) => index + dateStart[1]); + } else if (selectedYear == dateEnd[0]) { + month = List.generate(dateEnd[1], (index) => index + 1); + } else { + month = List.generate(12, (index) => index + 1); + } + data[1] = useMonth && filterItems != null ? filterItems!(DateTypeKey.month, month) : month; + monthFixedExtentScrollController.jumpToItem(monthIndex > data[1].length - 1 ? data[1].length - 1 : monthIndex); + } + + void refreshDayDataAndController() { + /// 在刷新日数据时,年月数据已经是最新的 + var selectedYear = yearIndex + data[0][0]; + var selectedMonth = monthIndex + data[1][0]; + late List day; + if (dateEnd[0] == dateStart[0] && dateEnd[1] == dateStart[1]) { + day = List.generate(dateEnd[2] - dateStart[2] + 1, (index) => index + dateStart[2]); + } else if (selectedYear == dateStart[0] && selectedMonth == dateStart[1]) { + day = List.generate( + DateTime(selectedYear, selectedMonth + 1, 0).day - dateStart[2] + 1, (index) => index + dateStart[2]); + } else if (selectedYear == dateEnd[0] && selectedMonth == dateEnd[1]) { + day = List.generate(dateEnd[2], (index) => index + 1); + } else { + day = List.generate(DateTime(selectedYear, selectedMonth + 1, 0).day, (index) => index + 1); + } + data[2] = useDay && filterItems != null ? filterItems!(DateTypeKey.day, day) : day; + dayFixedExtentScrollController.jumpToItem(dayIndex > data[2].length - 1 ? data[2].length - 1 : dayIndex); + } + + void refreshWeekDayDataAndController() { + var date = DateTime(data[0][yearFixedExtentScrollController.selectedItem], + data[1][monthFixedExtentScrollController.selectedItem], data[2][dayFixedExtentScrollController.selectedItem]); + weekDayFixedExtentScrollController.jumpToItem(date.weekday - 1); + } + + void refreshTimeDataInitialAndController(int wheel) { + var selectedYear = yearIndex + data[0][0]; + var selectedMonth = monthIndex + data[1][0]; + var selectDay = dayIndex + data[2][0]; + if (wheel <= 2) { + refreshHourData(selectedYear: selectedYear, selectedMonth: selectedMonth, selectDay: selectDay); + refreshMinuteData(selectedYear: selectedYear, selectedMonth: selectedMonth, selectDay: selectDay); + } else { + refreshMinuteData(selectedYear: selectedYear, selectedMonth: selectedMonth, selectDay: selectDay); + } + refreshSecondData(selectedYear: selectedYear, selectedMonth: selectedMonth, selectDay: selectDay); + } + + void refreshHourData({required int selectedYear, required int selectedMonth, required int selectDay}) { + var selectHour = selectDay == data[2][0] ? 0 : hourIndex; + late List hour; + if (selectedYear == dateStart[0] && selectedMonth == dateStart[1] && selectDay == dateStart[2]) { + hour = List.generate(24 - (dateStart[3]), (index) => index + dateStart[3]); + } else if (selectedYear == dateEnd[0] && selectedMonth == dateEnd[1] && selectDay == dateEnd[2]) { + hour = dateEnd[3] >= dateStart[3] + ? List.generate(dateEnd[3] + 1, (index) => index) + : List.generate(24 - dateStart[3], (index) => index); + } else { + hour = List.generate(24, (index) => index); + } + data[4] = useHour && filterItems != null ? filterItems!(DateTypeKey.hour, hour) : hour; + hourFixedExtentScrollController.jumpToItem(selectHour > 0 ? hourIndex : 0); + } + + void refreshMinuteData({required int selectedYear, required int selectedMonth, required int selectDay}) { + var selectHour = hourIndex + data[4][0]; + late List minute; + if (selectedYear == dateStart[0] && + selectedMonth == dateStart[1] && + selectDay == dateStart[2] && + selectHour == dateStart[3]) { + minute = List.generate(60 - (dateStart[4]), (index) => index + dateStart[4]); + } else if (selectedYear == dateEnd[0] && + selectedMonth == dateEnd[1] && + selectDay == dateEnd[2] && + selectHour == dateEnd[3]) { + minute = dateEnd[4] >= dateStart[4] + ? List.generate(dateEnd[4] + 1, (index) => index) + : List.generate(60 - (dateStart[4]), (index) => index); + } else { + minute = List.generate(60, (index) => index); + } + data[5] = useMinute && filterItems != null ? filterItems!(DateTypeKey.minute, minute) : minute; + } + + void refreshSecondData({required int selectedYear, required int selectedMonth, required int selectDay}) { + var selectHour = hourIndex + data[4][0]; + var selectMinute = minuteIndex + data[5][0]; + late List second; + if (selectedYear == dateStart[0] && + selectedMonth == dateStart[1] && + selectDay == dateStart[2] && + selectHour == dateStart[3] && + selectMinute == dateStart[4]) { + second = List.generate(60 - (dateStart[5]), (index) => index + dateStart[5]); + } else if (selectedYear == dateEnd[0] && + selectedMonth == dateEnd[1] && + selectDay == dateEnd[2] && + selectHour == dateEnd[3] && + selectMinute == dateEnd[4]) { + second = dateEnd[5] >= dateStart[5] + ? List.generate(dateEnd[5] + 1, (index) => index) + : List.generate(60 - (dateStart[5]), (index) => index); + } else { + second = List.generate(60, (index) => index); + } + data[6] = useSecond && filterItems != null ? filterItems!(DateTypeKey.second, second) : second; + } + + Map getSelectedMap() { + var map = { + 'year': yearIndex + data[0][0], + 'month': monthIndex + data[1][0], + 'day': dayIndex + data[2][0], + }; + return map; + } +} diff --git a/tdesign-component/lib/src/components/picker/td_item_widget.dart b/tdesign-component/lib/src/components/picker/td_item_widget.dart new file mode 100644 index 000000000..703d1b9ed --- /dev/null +++ b/tdesign-component/lib/src/components/picker/td_item_widget.dart @@ -0,0 +1,118 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +typedef ItemBuilderType = Widget? Function( + /// 上下文 + BuildContext context, + /// 文字内容 + String content, + /// 列号 + int colIndex, + /// 行号 + int index, + /// 根据距离计算字体颜色、透明度、粗细 + ItemDistanceCalculator itemDistanceCalculator, + /// 子项此时离中心的距离 + double distance, +); + +/// 所有选择器的子项组件 +class TDItemWidget extends StatefulWidget { + final String content; + final FixedExtentScrollController fixedExtentScrollController; + final int colIndex; + final int index; + final double itemHeight; + final ItemDistanceCalculator? itemDistanceCalculator; + final ItemBuilderType? itemBuilder; + + const TDItemWidget( + {required this.fixedExtentScrollController, + required this.colIndex, + required this.index, + required this.content, + required this.itemHeight, + this.itemDistanceCalculator, + this.itemBuilder, + Key? key}) + : super(key: key); + + @override + _TDItemWidgetState createState() => _TDItemWidgetState(); +} + +class _TDItemWidgetState extends State { + /// 子项监听滚动,从而刷新自身的颜色 + VoidCallback? listener; + ItemDistanceCalculator? _itemDistanceCalculator; + + @override + void initState() { + super.initState(); + listener = () => setState(() {}); + _itemDistanceCalculator = widget.itemDistanceCalculator; + + /// 子项注册滚动监听 + widget.fixedExtentScrollController.addListener(listener!); + } + + @override + Widget build(BuildContext context) { + /// 子项此时离中心的距离 + /// 不要使用widget.fixedExtentScrollController.selectedItem + /// 其中selectedItem会报错,原因是一开始minScrollExtent为空 + var distance = (widget.fixedExtentScrollController.offset / widget.itemHeight - widget.index).abs().toDouble(); + _itemDistanceCalculator ??= ItemDistanceCalculator(); + return widget.itemBuilder?.call( + context, + widget.content, + widget.colIndex, + widget.index, + _itemDistanceCalculator!, + distance, + ) ?? + TDText( + widget.content, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontWeight: _itemDistanceCalculator!.calculateFontWeight(context, distance), + fontSize: _itemDistanceCalculator!.calculateFont(context, distance), + color: _itemDistanceCalculator!.calculateColor(context, distance), + ), + ); + } + + @override + void dispose() { + /// 在销毁前完成监听注销 + widget.fixedExtentScrollController.removeListener(listener!); + super.dispose(); + } +} + +class ItemDistanceCalculator { + + ItemDistanceCalculator(); + + Color calculateColor(BuildContext context, double distance) { + /// 线性插值 + if (distance < 0.5) { + return TDTheme.of(context).fontGyColor1; + } else { + return TDTheme.of(context).fontGyColor4; + } + } + + FontWeight calculateFontWeight(BuildContext context, double distance) { + if (distance < 0.5) { + return FontWeight.w600; + } else { + return FontWeight.w400; + } + } + + double calculateFont(BuildContext context, double distance) { + return TDTheme.of(context).fontBodyLarge!.size; + } +} diff --git a/tdesign-component/lib/src/components/picker/td_multi_picker.dart b/tdesign-component/lib/src/components/picker/td_multi_picker.dart new file mode 100644 index 000000000..8b2d826f3 --- /dev/null +++ b/tdesign-component/lib/src/components/picker/td_multi_picker.dart @@ -0,0 +1,833 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +import '../../util/context_extension.dart'; +import 'no_wave_behavior.dart'; + +typedef MultiPickerCallback = void Function(List selected); + +/// 项之间无联动的多项选择器 +class TDMultiPicker extends StatelessWidget { + /// 选择器标题 + final String? title; + + /// 选择器确认按钮回调 + final MultiPickerCallback? onConfirm; + + /// 选择器取消按钮回调 + final MultiPickerCallback? onCancel; + + /// 选择器的数据源 + final List> data; + + /// 选择器List的视窗高度,默认200 + final double pickerHeight; + + /// 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 + final int pickerItemCount; + + /// 自定义选择框样式 + final Widget? customSelectWidget; + + /// 右侧按钮文案 + final String? rightText; + + /// 左侧按钮文案 + final String? leftText; + + /// 自定义左侧文案样式 + final TextStyle? leftTextStyle; + + /// 自定义右侧文案样式 + final TextStyle? rightTextStyle; + + /// 自定义中间文案样式 + final TextStyle? centerTextStyle; + + /// 标题高度 + final double? titleHeight; + + /// 顶部填充 + final double? topPadding; + + /// 左边填充 + final double? leftPadding; + + /// 右边填充 + final double? rightPadding; + + /// 标题分割线颜色 + final Color? titleDividerColor; + + /// 背景颜色 + final Color? backgroundColor; + + /// 顶部圆角 + final double? topRadius; + + /// 不同距离自选项计算策略 + final ItemDistanceCalculator? itemDistanceCalculator; + + /// 适配padding + final EdgeInsets? padding; + + /// 若为null表示全部从零开始 + final List? initialIndexes; + + /// 自定义item构建 + final ItemBuilderType? itemBuilder; + + static const _pickerTitleHeight = 56.0; + + const TDMultiPicker( + {required this.title, + required this.onConfirm, + this.onCancel, + required this.data, + required this.pickerHeight, + required this.pickerItemCount, + this.initialIndexes, + this.rightText, + this.leftText, + this.leftTextStyle, + this.rightTextStyle, + this.centerTextStyle, + this.titleHeight, + this.topPadding, + this.leftPadding, + this.rightPadding, + this.titleDividerColor, + this.backgroundColor, + this.topRadius, + this.padding, + this.itemDistanceCalculator, + this.customSelectWidget, + this.itemBuilder, + Key? key}) + : super(key: key); + + @override + Widget build(BuildContext context) { + var lines = data.length; + var indexes = initialIndexes ?? [for (var i = 0; i < lines; i++) 0]; + var controllers = [ + for (var i = 0; i < lines; i++) + FixedExtentScrollController(initialItem: indexes[i]) + ]; + var maxWidth = MediaQuery.of(context).size.width; + return Container( + width: maxWidth, + padding: padding ?? EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + decoration: BoxDecoration( + color: backgroundColor ?? TDTheme.of(context).whiteColor1, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(topRadius ?? TDTheme.of(context).radiusExtraLarge), + topRight: Radius.circular(topRadius ?? TDTheme.of(context).radiusExtraLarge), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + buildTitle(context, controllers), + Stack( + alignment: Alignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: customSelectWidget ?? Container( + height: 40, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.all(Radius.circular(TDTheme.of(context).radiusDefault)) + ), + ), + ), + // 列表 + Container( + padding: const EdgeInsets.only(left: 32, right: 32), + height: pickerHeight, + width: maxWidth, + child: Row( + children: [ + for (var i = 0; i < data.length; i++) + Expanded( + child: buildList(context, i, controllers), + ) + ], + )), + // 蒙层 + Positioned( + top: 0, + child: IgnorePointer( + ignoring: true, + child: Container( + height: _pickerTitleHeight, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [TDTheme.of(context).whiteColor1, TDTheme.of(context).whiteColor1.withOpacity(0)] + ) + ), + ), + ), + ), + Positioned( + bottom: 0, + child: IgnorePointer( + ignoring: true, + child: Container( + height: _pickerTitleHeight, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [TDTheme.of(context).whiteColor1, TDTheme.of(context).whiteColor1.withOpacity(0)] + ) + ), + ), + ), + ) + ], + ), + ], + ), + ); + } + + Widget buildTitle(BuildContext context, List controllers) { + return Container( + padding: EdgeInsets.only( + left: leftPadding ?? 16, + right: rightPadding ?? 16, + top: topPadding ?? 16, + ), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 0.5, + color: titleDividerColor ?? Colors.transparent, + ) + ), + ), + height: getTitleHeight(), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // 左边按钮 + GestureDetector( + onTap: () { + if (onCancel != null) { + onCancel!([ + for (var i = 0; i < controllers.length; i++) + controllers[i].selectedItem + ]); + } else { + Navigator.of(context).pop(); + } + }, + behavior: HitTestBehavior.opaque, + child: TDText( + leftText ?? context.resource.cancel, + style: leftTextStyle?? TextStyle( + fontSize: TDTheme.of(context).fontBodyLarge!.size, + color: TDTheme.of(context).fontGyColor2 + ), + )), + + // 中间title + Expanded( + child: title == null + ? Container() + : Center( + child: TDText( + title, + style: centerTextStyle ?? TextStyle( + fontSize: TDTheme.of(context).fontTitleLarge!.size, + fontWeight: FontWeight.w600, + color: TDTheme.of(context).fontGyColor1 + ), + ), + ), + ), + + // 右边按钮 + GestureDetector( + onTap: () { + if (onConfirm != null) { + onConfirm!([ + for (var i = 0; i < controllers.length; i++) + controllers[i].selectedItem + ]); + } + }, + behavior: HitTestBehavior.opaque, + child: TDText( + rightText ?? context.resource.confirm, + style: rightTextStyle?? TextStyle( + fontSize: TDTheme.of(context).fontBodyLarge!.size, + color: TDTheme.of(context).brandNormalColor + ), + ), + ), + ], + ), + ); + } + + double getTitleHeight() => titleHeight ?? _pickerTitleHeight; + + Widget buildList(context, int position, List controllers) { + var maxWidth = MediaQuery.of(context).size.width; + return MediaQuery.removePadding( + context: context, + removeTop: true, + child: ScrollConfiguration( + behavior: NoWaveBehavior(), + child: ListWheelScrollView.useDelegate( + itemExtent: pickerHeight / pickerItemCount, + diameterRatio: 100, + controller: controllers[position], + physics: const FixedExtentScrollPhysics(), + childDelegate: ListWheelChildBuilderDelegate( + childCount: data[position].length, + builder: (context, index) { + return Container( + key: UniqueKey(), + alignment: Alignment.center, + height: pickerHeight / pickerItemCount, + width: maxWidth, + child: TDItemWidget( + colIndex: position, + index: index, + key: UniqueKey(), + itemHeight: pickerHeight / pickerItemCount, + content: data[position][index], + itemDistanceCalculator: itemDistanceCalculator, + fixedExtentScrollController: controllers[position], + itemBuilder: itemBuilder, + ) + ); + })), + )); + } +} + +/// 多项联动选择器 +class TDMultiLinkedPicker extends StatefulWidget { + /// 选择器标题 + final String? title; + + /// 选择器确认按钮回调 + final MultiPickerCallback? onConfirm; + + /// 选择器取消按钮回调 + final MultiPickerCallback? onCancel; + + /// 选中数据 + final List selectedData; + + /// 选择器的数据源 + final Map data; + + /// 最大列数 + final int columnNum; + + /// 选择器List的视窗高度 + final double pickerHeight; + + /// 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 + final int pickerItemCount; + + /// 自定义选择框样式 + final Widget? customSelectWidget; + + /// 右侧按钮文案 + final String? rightText; + + /// 左侧按钮文案 + final String? leftText; + + /// 自定义左侧文案样式 + final TextStyle? leftTextStyle; + + /// 自定义右侧文案样式 + final TextStyle? rightTextStyle; + + /// 自定义中间文案样式 + final TextStyle? centerTextStyle; + + /// 适配padding + final EdgeInsets? padding; + + /// 标题高度 + final double? titleHeight; + + /// 顶部填充 + final double? topPadding; + + /// 左边填充 + final double? leftPadding; + + /// 右边填充 + final double? rightPadding; + + /// 标题分割线颜色 + final Color? titleDividerColor; + + /// 背景颜色 + final Color? backgroundColor; + + /// 顶部圆角 + final double? topRadius; + + /// 不同距离自选项计算策略 + final ItemDistanceCalculator? itemDistanceCalculator; + + /// 自定义item构建 + final ItemBuilderType? itemBuilder; + + /// 是否保留相同选项 + final bool keepSameSelection; + + const TDMultiLinkedPicker({ + this.title, + required this.onConfirm, + this.onCancel, + required this.selectedData, + required this.data, + required this.columnNum, + this.pickerHeight = 200, + this.pickerItemCount = 5, + this.customSelectWidget, + this.rightText, + this.leftText, + this.leftTextStyle, + this.rightTextStyle, + this.centerTextStyle, + this.titleHeight, + this.topPadding, + this.leftPadding, + this.rightPadding, + this.titleDividerColor, + this.backgroundColor, + this.topRadius, + this.padding, + this.itemDistanceCalculator, + this.itemBuilder, + this.keepSameSelection = false, + Key? key, + }) : super(key: key); + + @override + State createState() => _TDMultiLinkedPickerState(); +} + +class _TDMultiLinkedPickerState extends State { + late MultiLinkedPickerModel model; + + double pickerHeight = 0; + + static const _pickerTitleHeight = 56.0; + + @override + void initState() { + super.initState(); + pickerHeight = widget.pickerHeight; + model = MultiLinkedPickerModel( + data: widget.data, columnNum: widget.columnNum, initialData: widget.selectedData, keepSameSelection: widget.keepSameSelection); + } + + @override + Widget build(BuildContext context) { + var maxWidth = MediaQuery.of(context).size.width; + return Container( + width: maxWidth, + padding: widget.padding ?? EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + decoration: BoxDecoration( + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(widget.topRadius ?? TDTheme.of(context).radiusExtraLarge), + topRight: Radius.circular(widget.topRadius ?? TDTheme.of(context).radiusExtraLarge), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + buildTitle(context), + SizedBox( + height: widget.pickerHeight, + child: Stack( + alignment: Alignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: widget.customSelectWidget ?? Container( + height: 40, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.all(Radius.circular(TDTheme.of(context).radiusDefault)) + ), + ), + ), + + // 列表 + Container( + padding: const EdgeInsets.only(left: 32, right: 32), + height: pickerHeight, + width: maxWidth, + child: Row( + children: [ + for (var i = 0; i < widget.columnNum; i++) + Expanded( + child: buildList(context, i), + ) + ], + )), + // 蒙层 + Positioned( + top: 0, + child: IgnorePointer( + ignoring: true, + child: Container( + height: _pickerTitleHeight, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [TDTheme.of(context).whiteColor1, TDTheme.of(context).whiteColor1.withOpacity(0)] + ) + ), + ), + ), + ), + Positioned( + bottom: 0, + child: IgnorePointer( + ignoring: true, + child: Container( + height: _pickerTitleHeight, + width: MediaQuery.of(context).size.width, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [TDTheme.of(context).whiteColor1, TDTheme.of(context).whiteColor1.withOpacity(0)] + ) + ), + ), + ), + ) + ], + ), + ) + ], + ), + ); + } + + Widget buildList(context, int position) { + // position参数表示这个第几列 + var maxWidth = MediaQuery.of(context).size.width; + return MediaQuery.removePadding( + context: context, + removeTop: true, + child: ScrollConfiguration( + behavior: NoWaveBehavior(), + child: ListWheelScrollView.useDelegate( + itemExtent: pickerHeight / widget.pickerItemCount, + diameterRatio: 100, + controller: model.controllers[position], + physics: const FixedExtentScrollPhysics(), + onSelectedItemChanged: (index) { + setState(() { + // 刷新此列右边的所有数据 + model.refreshPresentDataAndController(position, index, false); + + // 使用动态高度,强制列表组件的state刷新,以展现更新的数据,详见下方链接 + // FIX:https://github.com/flutter/flutter/issues/22999 + pickerHeight = + pickerHeight - Random().nextDouble() / 100000000; + }); + }, + childDelegate: ListWheelChildBuilderDelegate( + childCount: model.presentData[position].length, + builder: (context, index) { + return Container( + alignment: Alignment.center, + height: pickerHeight / widget.pickerItemCount, + width: maxWidth, + child: TDItemWidget( + colIndex: position, + index: index, + itemHeight: pickerHeight / widget.pickerItemCount, + content: + model.presentData[position][index].toString(), + fixedExtentScrollController: + model.controllers[position], + itemDistanceCalculator: widget.itemDistanceCalculator, + itemBuilder: widget.itemBuilder, + )); + })), + )); + } + + Widget buildTitle(BuildContext context) { + return Container( + padding: EdgeInsets.only( + left: widget.leftPadding ?? 16, + right: widget.rightPadding ?? 16, + top: widget.topPadding ?? 16, + ), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 0.5, + color: widget.titleDividerColor ?? Colors.transparent, + ) + ) + ), + height: getTitleHeight() - 0.5, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // 左边按钮 + GestureDetector( + onTap: () { + if (widget.onCancel != null) { + widget.onCancel!(model.selectedData); + } else { + Navigator.of(context).pop(); + } + }, + behavior: HitTestBehavior.opaque, + child: TDText( + widget.leftText ?? context.resource.cancel, + style: widget.leftTextStyle ?? TextStyle( + fontSize: TDTheme.of(context).fontBodyLarge!.size, + color: TDTheme.of(context).fontGyColor2, + ), + )), + + // 中间title + Expanded( + child: widget.title == null + ? Container() + : Center( + child: TDText( + widget.title, + style: widget.centerTextStyle ?? TextStyle( + fontSize: TDTheme.of(context).fontTitleLarge!.size, + fontWeight: FontWeight.w700, + color: TDTheme.of(context).fontGyColor1 + ), + ), + ), + ), + + // 右边按钮 + GestureDetector( + onTap: () { + if (widget.onConfirm != null) { + widget.onConfirm!(model.selectedData); + } + }, + behavior: HitTestBehavior.opaque, + child: TDText( + widget.rightText ?? context.resource.confirm, + style: widget.rightTextStyle ?? TextStyle( + fontSize: TDTheme.of(context).fontBodyLarge!.size, + color: TDTheme.of(context).brandNormalColor, + ), + ), + ), + ], + ), + ); + } + + double getTitleHeight() => widget.titleHeight ?? _pickerTitleHeight; +} + +class MultiLinkedPickerModel { + /// 占位字符 + static const placeData = ''; + + /// 总的数据 + late Map data; + + /// 选中数据下标 + late List selectedIndexes; + + /// 总列数 + late int columnNum; + + /// 选中数据 + late List selectedData; + + late List controllers = []; + + /// 每一列展示的数据 + late List presentData = []; + + /// 是否保留相同选项 + bool keepSameSelection = false; + + MultiLinkedPickerModel({ + required this.data, + required this.columnNum, + required List initialData, + this.keepSameSelection = false, + }) { + selectedData = []; + selectedIndexes = []; + for (var i = 0; i < columnNum; ++i) { + if (i >= initialData.length) { + selectedData.add(''); + } else { + selectedData.add(initialData[i]); + } + selectedIndexes.add(0); + } + _init(initialData); + } + + void _init(List initialData) { + int pIndex; + controllers.clear(); + presentData.clear(); + for (var i = 0; i < columnNum; ++i) { + pIndex = 0; + if (i == 0) { + // 第一列 + pIndex = data.keys.toList().indexOf(selectedData[i]); + if (pIndex < 0) { + selectedData[i] = data.keys.first; + pIndex = 0; + } + selectedIndexes[i] = pIndex; + presentData.add(data.keys.toList()); + } else { + // 其他列 + dynamic date = findNextData(i); + if (date is Map) { + pIndex = date.keys.toList().indexOf(selectedData[i]); + if (pIndex < 0) { + selectedData[i] = date.keys.first; + pIndex = 0; + } + presentData.add(date.keys.toList()); + } else if (date is List) { + pIndex = date.indexOf(selectedData[i]); + if (pIndex < 0) { + selectedData[i] = date.first; + pIndex = 0; + } + presentData.add(date); + } else { + selectedData[i] = date; + pIndex = 0; + presentData.add([date]); + } + selectedIndexes[i] = pIndex; + } + controllers.add(FixedExtentScrollController(initialItem: pIndex)); + } + } + /// 对应位置的下一列数据 + dynamic findNextData(int position) { + dynamic nextData; + for (var i = 0; i < position; i++) { + if (i == 0) { + nextData = data[selectedData[0]]; + } else { + dynamic data = nextData[selectedData[i]]; + if (data is Map) { + nextData = data; + } else if (data is List) { + nextData = data; + } else { + nextData = [data]; + } + } + if (!(nextData is Map) && (i < position - 1)) { + return [placeData]; + } + } + return nextData; + } + + /// [position] 变动的列 + /// [selectedIndex] 对应选中的index + /// [jump] 是否需要jumpToItem + void refreshPresentDataAndController(int position, int selectedIndex, bool jump) { + // 新选中的数据 + var selectValue = presentData[position][selectedIndex]; + // 更新选中的数据 + selectedData[position] = selectValue; + selectedIndexes[position] = selectedIndex; + if (jump) { + controllers[position].jumpToItem(selectedIndex); + } + // 如果不是最后一列 数据的变动都会造成剩下列的更新 + if (position < columnNum - 1) { + if (presentData[position].length == 1 && presentData[position].first == placeData) { + presentData[position + 1] = [placeData]; + } else { + presentData[position + 1] = findColumnData(position + 1); + } + + // 新增保留相同选项逻辑 + if (keepSameSelection && position + 1 < selectedData.length) { + var nextPosition = position + 1; + var oldSelected = selectedData[nextPosition]; + var newDataList = presentData[nextPosition]; + + // 查找旧值是否存在于新数据列表中 + var index = newDataList.indexOf(oldSelected); + if (index != -1) { + // 存在相同值则保留原选择 + refreshPresentDataAndController(nextPosition, index, true); + return; + } + } + + refreshPresentDataAndController(position + 1, 0, true); + } + } + + /// 寻找对应位置数据 + List findColumnData(int position) { + dynamic nextData; + for (var i = 0; i < position; i++) { + if (i == 0) { + nextData = data[selectedData[0]]; + } else { + dynamic data = nextData[selectedData[i]]; + if (data is Map) { + nextData = data; + } else if (data is List) { + nextData = data; + } else { + nextData = [data]; + } + } + // 如果是map并且是最后一列 返回对应key + if ((nextData is Map) && (i == position - 1)) { + return nextData.keys.toList(); + } + + // 如果数据还没有到最后就已经不是Map + if (!(nextData is Map) && (i < position - 1)) { + return [placeData]; + } + } + return nextData; + } + +} diff --git a/tdesign-component/lib/src/components/picker/td_picker.dart b/tdesign-component/lib/src/components/picker/td_picker.dart new file mode 100644 index 000000000..5f381ec7e --- /dev/null +++ b/tdesign-component/lib/src/components/picker/td_picker.dart @@ -0,0 +1,174 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +class TDPicker { + + /// 显示时间选择器 + static void showDatePicker(context, + {required String title, + required DatePickerCallback? onConfirm, + DatePickerCallback? onCancel, + bool useYear = true, + bool useMonth = true, + bool useDay = true, + bool useHour = false, + bool useMinute = false, + bool useSecond = false, + bool useWeekDay = false, + Color? barrierColor, + List dateStart = const [1970, 1, 1], + List? dateEnd, + List? initialDate, + String? rightText, + String? leftText, + TextStyle? leftTextStyle, + TextStyle? centerTextStyle, + TextStyle? rightTextStyle, + Color? titleDividerColor, + Widget? customSelectWidget, + Duration duration = const Duration(milliseconds: 100), + double pickerHeight = 200, + bool isTimeUnit=true, + Function(int wheelIndex, int index)? onSelectedItemChanged, + int pickerItemCount = 5, + List Function(DateTypeKey key, List nums)? filterItems, + ItemBuilderType? itemBuilder}) { + if (dateEnd == null || initialDate == null) { + var now = DateTime.now(); + // 如果未指定结束时间,则取当前时间 + dateEnd ??= [now.year, now.month, now.day]; + initialDate ??= [now.year, now.month, now.day]; + } + showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + barrierColor: barrierColor ?? TDTheme.of(context).fontGyColor2.withOpacity(0.6), + enableDrag:false, + builder: (context) { + return TDDatePicker( + title: title, + onConfirm: onConfirm, + onCancel: onCancel, + rightText: rightText, + leftText: leftText, + leftTextStyle: leftTextStyle, + centerTextStyle: centerTextStyle, + rightTextStyle: rightTextStyle, + titleDividerColor: titleDividerColor, + isTimeUnit: isTimeUnit, + customSelectWidget: customSelectWidget, + model: DatePickerModel( + useYear: useYear, + useMonth: useMonth, + useDay: useDay, + useWeekDay: useWeekDay, + useHour: useHour, + useMinute: useMinute, + useSecond: useSecond, + dateStart: dateStart, + dateEnd: dateEnd!, + dateInitial: initialDate, + filterItems: filterItems, + ), + pickerHeight: pickerHeight, + pickerItemCount: pickerItemCount, + onSelectedItemChanged:onSelectedItemChanged, + itemBuilder: itemBuilder); + }); + } + + /// 显示多级选择器 + static void showMultiPicker(context, + {String? title, + required MultiPickerCallback? onConfirm, + MultiPickerCallback? onCancel, + required List> data, + List? initialIndexes, + Duration duration = const Duration(milliseconds: 100), + Color? barrierColor, + double pickerHeight = 200, + String? rightText, + String? leftText, + TextStyle? leftTextStyle, + TextStyle? centerTextStyle, + TextStyle? rightTextStyle, + Color? titleDividerColor, + double? topPadding, + int pickerItemCount = 5, + Widget? customSelectWidget, + ItemBuilderType? itemBuilder}) { + showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + barrierColor: barrierColor ?? TDTheme.of(context).fontGyColor2.withOpacity(0.6), + builder: (context) { + return TDMultiPicker( + title: title, + onConfirm: onConfirm, + onCancel: onCancel, + data: data, + rightText: rightText, + leftText: leftText, + leftTextStyle: leftTextStyle, + centerTextStyle: centerTextStyle, + rightTextStyle: rightTextStyle, + initialIndexes: initialIndexes, + pickerHeight: pickerHeight, + pickerItemCount: pickerItemCount, + titleDividerColor: titleDividerColor, + topPadding: topPadding, + itemBuilder: itemBuilder, + customSelectWidget: customSelectWidget, + ); + }); + } + + /// 显示多级联动选择器 + static void showMultiLinkedPicker(context, + {String? title, + required MultiPickerCallback? onConfirm, + MultiPickerCallback? onCancel, + required Map data, + required int columnNum, + required List initialData, + Duration duration = const Duration(milliseconds: 100), + Color? barrierColor, + String? rightText, + String? leftText, + TextStyle? leftTextStyle, + TextStyle? centerTextStyle, + TextStyle? rightTextStyle, + double pickerHeight = 200, + Color? titleDividerColor, + Widget? customSelectWidget, + double? topPadding, + bool keepSameSelection = false, + int pickerItemCount = 5}) { + showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + barrierColor: barrierColor ?? TDTheme.of(context).fontGyColor2.withOpacity(0.6), + builder: (context) { + return TDMultiLinkedPicker( + title: title, + onConfirm: onConfirm, + onCancel: onCancel, + data: data, + rightText: rightText, + leftText: leftText, + leftTextStyle: leftTextStyle, + centerTextStyle: centerTextStyle, + rightTextStyle: rightTextStyle, + pickerHeight: pickerHeight, + pickerItemCount: pickerItemCount, + columnNum: columnNum, + selectedData: initialData, + titleDividerColor: titleDividerColor, + topPadding: topPadding, + customSelectWidget: customSelectWidget, + keepSameSelection: keepSameSelection, + ); + }); + } +} diff --git a/tdesign-component/lib/src/components/popover/td_popover.dart b/tdesign-component/lib/src/components/popover/td_popover.dart new file mode 100644 index 000000000..52b800e6c --- /dev/null +++ b/tdesign-component/lib/src/components/popover/td_popover.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +class TDPopover { + + static Future showPopover({ + required BuildContext context, + String? content, + Widget? contentWidget, + double offset = 4, + TDPopoverTheme? theme, + bool closeOnClickOutside = true, + TDPopoverPlacement? placement, + bool? showArrow = true, + double arrowSize = 8, + EdgeInsetsGeometry? padding, + double? width, + double? height, + Color? overlayColor = Colors.transparent, + OnTap? onTap, + OnLongTap? onLongTap, + }) { + return showDialog( + barrierDismissible: closeOnClickOutside, + barrierColor: overlayColor, + useSafeArea: false, + context: context, + builder: (ctx) => TDPopoverWidget( + context: context, + content: content, + contentWidget: contentWidget, + offset: offset, + theme: theme, + placement: placement, + showArrow: showArrow, + arrowSize: arrowSize, + padding: padding, + width: width, + height: height, + onTap: onTap, + onLongTap: onLongTap, + ), + ); + } +} \ No newline at end of file diff --git a/tdesign-component/lib/src/components/popover/td_popover_widget.dart b/tdesign-component/lib/src/components/popover/td_popover_widget.dart new file mode 100644 index 000000000..62573c3c6 --- /dev/null +++ b/tdesign-component/lib/src/components/popover/td_popover_widget.dart @@ -0,0 +1,482 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +enum TDPopoverTheme { + /// 暗色 + dark, + + /// 亮色 + light, + + /// 品牌色 + info, + + /// 成功 + success, + + /// 警告 + warning, + + /// 错误 + error +} + +enum TDPopoverPlacement { + /// 上左 + topLeft, + + /// 上 + top, + + /// 上右 + topRight, + + /// 右上 + rightTop, + + /// 右 + right, + + /// 右下 + rightBottom, + + /// 下右 + bottomRight, + + /// 下 + bottom, + + /// 下左 + bottomLeft, + + /// 左下 + leftBottom, + + /// 左 + left, + + /// 左上 + leftTop +} + +typedef OnTap = Function(String? content); +typedef OnLongTap = Function(String? content); + +class TDPopoverWidget extends StatefulWidget { + const TDPopoverWidget({ + super.key, + required this.context, + this.content, + this.contentWidget, + this.offset = 4, + this.theme, + this.placement, + this.showArrow = true, + this.arrowSize = 8, + this.padding, + this.width, + this.height, + this.onTap, + this.onLongTap, + }); + + /// 上下文 + final BuildContext context; + + /// 显示内容 + final String? content; + + /// 自定义内容 + final Widget? contentWidget; + + /// 偏移 + final double offset; + + /// 弹出气泡主题 + final TDPopoverTheme? theme; + + /// 浮层出现位置 + final TDPopoverPlacement? placement; + + /// 是否显示浮层箭头 + final bool? showArrow; + + /// 箭头大小 + final double arrowSize; + + /// 内容内边距 + final EdgeInsetsGeometry? padding; + + /// 内容宽度(包含padding,实际高度:height - paddingLeft - paddingRight) + final double? width; + + /// 内容高度(包含padding,实际高度:height - paddingTop - paddingBottom) + final double? height; + + /// 点击事件 + final OnTap? onTap; + + /// 长按事件 + final OnLongTap? onLongTap; + + @override + State createState() => _TDPopoverWidgetState(); +} + +class _TDPopoverWidgetState extends State { + late Color _color; + + late Color _backgroundColor; + + @override + void initState() { + super.initState(); + _initTheme(); + if(widget.contentWidget != null) { + if(widget.width == null) { + throw FlutterError('width must not be null when contentWidget is not null'); + } + if(widget.height == null) { + throw FlutterError('height must not be null when contentWidget is not null'); + } + } + } + + /// 绘制箭头 + Widget _drawArrow() { + var border = Border( + right: BorderSide(width: widget.arrowSize, color: Colors.transparent, style: BorderStyle.solid), + bottom: BorderSide(width: widget.arrowSize, color: _backgroundColor, style: BorderStyle.solid), + left: BorderSide(width: widget.arrowSize, color: Colors.transparent, style: BorderStyle.solid) + ); + if(widget.placement == TDPopoverPlacement.bottom || widget.placement == TDPopoverPlacement.bottomLeft || widget.placement == TDPopoverPlacement.bottomRight) { + border = Border( + top: BorderSide(width: widget.arrowSize, color: _backgroundColor, style: BorderStyle.solid), + right: BorderSide(width: widget.arrowSize, color: Colors.transparent, style: BorderStyle.solid), + left: BorderSide(width: widget.arrowSize, color: Colors.transparent, style: BorderStyle.solid) + ); + } else if(widget.placement == TDPopoverPlacement.left || widget.placement == TDPopoverPlacement.leftTop || widget.placement == TDPopoverPlacement.leftBottom) { + border = Border( + top: BorderSide(width: widget.arrowSize, color: Colors.transparent, style: BorderStyle.solid), + bottom: BorderSide(width: widget.arrowSize, color: Colors.transparent, style: BorderStyle.solid), + right: BorderSide(width: widget.arrowSize, color: _backgroundColor, style: BorderStyle.solid) + ); + } else if(widget.placement == TDPopoverPlacement.right || widget.placement == TDPopoverPlacement.rightTop || widget.placement == TDPopoverPlacement.rightBottom) { + border = Border( + top: BorderSide(width: widget.arrowSize, color: Colors.transparent, style: BorderStyle.solid), + bottom: BorderSide(width: widget.arrowSize, color: Colors.transparent, style: BorderStyle.solid), + left: BorderSide(width: widget.arrowSize, color: _backgroundColor, style: BorderStyle.solid) + ); + } + return Container( + width: 0, + height: 0, + decoration: BoxDecoration(border: border), + ); + } + + /// 初始化主题 + void _initTheme() { + switch (widget.theme) { + case TDPopoverTheme.info: + _color = TDTheme.of(widget.context).brandNormalColor; + _backgroundColor = TDTheme.of(widget.context).brandLightColor; + break; + case TDPopoverTheme.success: + _color = TDTheme.of(widget.context).successNormalColor; + _backgroundColor = TDTheme.of(widget.context).successLightColor; + break; + case TDPopoverTheme.warning: + _color = TDTheme.of(widget.context).warningNormalColor; + _backgroundColor = TDTheme.of(widget.context).warningLightColor; + break; + case TDPopoverTheme.error: + _color = TDTheme.of(widget.context).errorNormalColor; + _backgroundColor = TDTheme.of(widget.context).errorLightColor; + break; + case TDPopoverTheme.light: + _color = TDTheme.of(widget.context).grayColor14; + _backgroundColor = TDTheme.of(widget.context).whiteColor1; + break; + default : + _color = TDTheme.of(widget.context).whiteColor1; + _backgroundColor = TDTheme.of(widget.context).grayColor14; + break; + } + } + + /// 获取触发元素大小 + Rect? _getWidgetBounds(BuildContext context) { + final box = context.findRenderObject() as RenderBox?; + return box?.semanticBounds; + } + + /// 获取触发元素坐标 + Offset? _getWidgetLocalToGlobal(BuildContext context) { + final box = context.findRenderObject() as RenderBox?; + return box?.localToGlobal(Offset.zero); + } + + /// 获取Y坐标 + double _getOffsetTop(Offset? widgetLocalToGlobal) { + var widgetBounds = _getWidgetBounds(widget.context); + var dy = widgetLocalToGlobal?.dy ?? 0; + var arrowSize = widget.showArrow ?? false ? widget.arrowSize : 0; + var contentSize = _getContentSize(); + var popoverHeight = widget.height ?? (widget.padding != null ? widget.padding!.vertical : 24) + (widget.height ?? contentSize.height); + switch (widget.placement) { + case TDPopoverPlacement.bottomLeft: + case TDPopoverPlacement.bottom: + case TDPopoverPlacement.bottomRight: + return dy + (widgetBounds?.height ?? 0) + widget.offset; + case TDPopoverPlacement.rightTop: + case TDPopoverPlacement.leftTop: + return dy; + case TDPopoverPlacement.rightBottom: + case TDPopoverPlacement.leftBottom: + return dy - (popoverHeight - (widgetBounds?.height ?? 0)); + case TDPopoverPlacement.right: + case TDPopoverPlacement.left: + return dy - (popoverHeight - (widgetBounds?.height ?? 0)) / 2; + default: + return dy - popoverHeight - widget.offset - arrowSize; + } + } + + /// 获取X坐标 + double _getOffsetLeft(Offset? widgetLocalToGlobal) { + var widgetBounds = _getWidgetBounds(widget.context); + var widgetWidth = widgetBounds?.width ?? 0; + var contentSize = _getContentSize(); + var popoverWidth = widget.width ?? (widget.padding != null ? widget.padding!.horizontal : 24) + contentSize.width; + var dx = widgetLocalToGlobal?.dx ?? 0; + switch (widget.placement) { + case TDPopoverPlacement.topLeft: + case TDPopoverPlacement.bottomLeft: + return dx; + case TDPopoverPlacement.topRight: + case TDPopoverPlacement.bottomRight: + return dx + widgetWidth - popoverWidth; + case TDPopoverPlacement.rightTop: + case TDPopoverPlacement.right: + case TDPopoverPlacement.rightBottom: + return dx + widgetWidth + widget.offset + 8; + case TDPopoverPlacement.leftTop: + case TDPopoverPlacement.left: + case TDPopoverPlacement.leftBottom: + return dx - popoverWidth - widget.arrowSize - widget.offset - 8; + default : + return dx - (popoverWidth - widgetWidth) / 2; + } + } + + /// 获取箭头Widget + Widget _getArrowWidget() { + var margin = EdgeInsets.only(top: widget.arrowSize); + switch (widget.placement) { + case TDPopoverPlacement.topLeft: + margin = EdgeInsets.only(top: widget.arrowSize, left: widget.arrowSize + 12); + break; + case TDPopoverPlacement.topRight: + margin = EdgeInsets.only(top: widget.arrowSize, right: widget.arrowSize + 12); + break; + case TDPopoverPlacement.bottomLeft: + margin = EdgeInsets.only(bottom: widget.arrowSize, left: widget.arrowSize + 12); + break; + case TDPopoverPlacement.bottom: + margin = EdgeInsets.only(bottom: widget.arrowSize); + break; + case TDPopoverPlacement.bottomRight: + margin = EdgeInsets.only(bottom: widget.arrowSize, right: widget.arrowSize + 12); + break; + case TDPopoverPlacement.rightTop: + margin = EdgeInsets.only(top: widget.arrowSize + 6, right: widget.arrowSize); + break; + case TDPopoverPlacement.right: + margin = EdgeInsets.only(right: widget.arrowSize); + break; + case TDPopoverPlacement.rightBottom: + margin = EdgeInsets.only(bottom: widget.arrowSize + 6, right: widget.arrowSize); + break; + case TDPopoverPlacement.leftTop: + margin = EdgeInsets.only(top: widget.arrowSize + 6, left: widget.arrowSize); + break; + case TDPopoverPlacement.left: + margin = EdgeInsets.only(left: widget.arrowSize); + break; + case TDPopoverPlacement.leftBottom: + margin = EdgeInsets.only(bottom: widget.arrowSize + 6, left: widget.arrowSize); + break; + default : + margin = EdgeInsets.only(top: widget.arrowSize); + } + return Container( + margin: margin, + child: _drawArrow(), + ); + } + + /// 获取弹出内容大小 + Size _getContentSize() { + if(widget.contentWidget != null) { + return Size(widget.width!, widget.height!); + } + return _getTextSize(); + } + + /// 获取文本内容大小 + Size _getTextSize() { + var textPainter = TextPainter( + text: TextSpan( + text: widget.content, + style: TextStyle( + color: _color, + letterSpacing: 0, + fontSize: 16, + height: 1.5, + ), + ), + locale: Localizations.localeOf(context), + textDirection: TextDirection.ltr, + )..layout(maxWidth: (widget.width ?? 300) - (widget.padding != null ? widget.padding!.horizontal : 24)); + return textPainter.size; + } + + /// 获取子Widget + Widget _getChild() { + var children = [ + Container( + width: widget.width, + height: widget.height, + padding: widget.padding ?? const EdgeInsets.all(12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + color: _backgroundColor, + boxShadow: const [ + BoxShadow(color: Color(0x0d000000), offset: Offset(0, 6), blurRadius: 30, spreadRadius: 5), + BoxShadow(color: Color(0x0a000000), offset: Offset(0, 16), blurRadius: 24, spreadRadius: 2), + BoxShadow(color: Color(0x14000000), offset: Offset(0, 8), blurRadius: 10, spreadRadius: -5), + ] + ), + child: widget.contentWidget != null ? widget.contentWidget! : TDText( + widget.content, + style: TextStyle( + color: _color, + letterSpacing: 0, + fontSize: 16, + height: 1.5, + ) + ), + ), + Visibility( + visible: widget.showArrow ?? false, + child: _getArrowWidget(), + ) + ]; + var axis = CrossAxisAlignment.center; + var direction = VerticalDirection.down; + + /// 设置子Widget垂直排列顺序 + switch (widget.placement) { + case TDPopoverPlacement.bottom: + case TDPopoverPlacement.bottomLeft: + case TDPopoverPlacement.bottomRight: + direction = VerticalDirection.up; + break; + case TDPopoverPlacement.right: + case TDPopoverPlacement.rightTop: + case TDPopoverPlacement.rightBottom: + /// 反转内容和箭头 + children = [ + Visibility( + visible: widget.showArrow ?? false, + child: Container( + child: _getArrowWidget(), + ), + ), + Container( + padding: widget.padding ?? const EdgeInsets.all(12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + color: _backgroundColor, + boxShadow: [ + BoxShadow(color: TDTheme.of(context).grayColor7, offset: const Offset(6, 3), blurRadius: 6) + ] + ), + child: widget.contentWidget != null ? widget.contentWidget! : TDText( + widget.content, + style: TextStyle( + color: _color, + letterSpacing: 0, + fontSize: 16, + height: 1.5, + overflow: TextOverflow.ellipsis, + ) + ), + ), + ]; + break; + default : + direction = VerticalDirection.down; + break; + } + + /// 改变Row和Column交叉轴对齐位置,从而实现箭头位置 + switch (widget.placement) { + case TDPopoverPlacement.topLeft: + case TDPopoverPlacement.bottomLeft: + axis = CrossAxisAlignment.start; + break; + case TDPopoverPlacement.topRight: + case TDPopoverPlacement.bottomRight: + axis = CrossAxisAlignment.end; + break; + case TDPopoverPlacement.rightTop: + case TDPopoverPlacement.leftTop: + axis = CrossAxisAlignment.start; + break; + case TDPopoverPlacement.rightBottom: + case TDPopoverPlacement.leftBottom: + axis = CrossAxisAlignment.end; + break; + default : + axis = CrossAxisAlignment.center; + } + + /// 横向布局 + if(widget.placement == TDPopoverPlacement.right || + widget.placement == TDPopoverPlacement.rightTop || + widget.placement == TDPopoverPlacement.rightBottom || + widget.placement == TDPopoverPlacement.left || + widget.placement == TDPopoverPlacement.leftBottom || + widget.placement == TDPopoverPlacement.leftTop) { + return Row( + crossAxisAlignment: axis, + children: children, + ); + } + /// 纵向布局 + return Column( + crossAxisAlignment: axis, + verticalDirection: direction, + children: children, + ); + } + + @override + Widget build(BuildContext context) { + _initTheme(); + var widgetLocalToGlobal = _getWidgetLocalToGlobal(widget.context); + var top = _getOffsetTop(widgetLocalToGlobal); + var left = _getOffsetLeft(widgetLocalToGlobal); + return Stack( + children: [ + Positioned( + top: top, + left: left, + child: _getChild(), + ), + ], + ); + } +} \ No newline at end of file diff --git a/tdesign-component/lib/src/components/popup/td_popup_panel.dart b/tdesign-component/lib/src/components/popup/td_popup_panel.dart new file mode 100644 index 000000000..fcf27128d --- /dev/null +++ b/tdesign-component/lib/src/components/popup/td_popup_panel.dart @@ -0,0 +1,551 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; + +typedef PopupClick = Function(); + +/// 弹窗基类 +abstract class TDPopupBasePanel extends StatefulWidget { + const TDPopupBasePanel({ + Key? key, + required this.child, + this.title, + this.titleColor, + this.backgroundColor, + this.radius, + this.draggable = false, + this.maxHeightRatio = 0.9, + this.minHeightRatio = 0.3, + }) : super(key: key); + + /// 子控件 + final Widget child; + /// 标题 + final String? title; + /// 标题颜色 + final Color? titleColor; + /// 背景颜色 + final Color? backgroundColor; + /// 圆角 + final double? radius; + /// 边缘是否可拖动 + final bool draggable; + /// 最大高度比例 + final double maxHeightRatio; + /// 最小高度比例 + final double minHeightRatio; + + @override + State createState(); +} + +abstract class _TDPopupBaseState extends State + with SingleTickerProviderStateMixin { + final GlobalKey _childKey = GlobalKey(); + static const _dragHandleHeight = 24.0; + static const _headerHeight = 58.0; + + late AnimationController _controller; + double _maxHeight = 0; + double _minHeight = 0; + double _currentHeight = 0; + bool _isFullscreen = false; + bool _isAnimating = false; + bool _isDragging = false; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(milliseconds: 400), + vsync: this, + )..addListener(_updateHeight); + WidgetsBinding.instance.addPostFrameCallback((_) => _measureChildHeight()); + } + + /// 测量子组件高度并更新弹窗布局参数 + /// 1.获取子组件渲染尺寸 + /// 2.计算实际需要的基础高度 + /// 3.动态计算计算最大最小高度比例 + /// 4.更新动画控制器状态 + void _measureChildHeight() { + // 获取子组件渲染对象 + final context = _childKey.currentContext; + if (context == null) return; + final renderBox = context.findRenderObject() as RenderBox?; + if (renderBox == null || !renderBox.hasSize) return; + + final screenHeight = MediaQuery.of(context).size.height; + final childHeight = renderBox.size.height; + final headerHeight = widget.draggable ? _headerHeight : _headerHeight; + final baseHeight = _dragHandleHeight + headerHeight + childHeight; + + // 动态计算最大最小高度 + final maxHeightByRatio = screenHeight * widget.maxHeightRatio; + final minHeightByRatio = screenHeight * widget.minHeightRatio; + + // 内容高度和比例约束 + _maxHeight = min(baseHeight, maxHeightByRatio); + _minHeight = max(baseHeight * 0.5, minHeightByRatio); + if (_minHeight > _maxHeight) { + _minHeight = _maxHeight; + } + + // 初始化当前高度 + _currentHeight = baseHeight.clamp(_minHeight, _maxHeight); + // 同步动画控制器 + _controller.value = (_currentHeight - _minHeight) / + (_maxHeight - _minHeight).clamp(0.1, 1.0); + } + + void _updateHeight() => setState(() { + _currentHeight = _minHeight + (_maxHeight - _minHeight) * _controller.value; + }); + + void _toggleFullscreen(bool fullscreen) { + if (_isAnimating || _isFullscreen == fullscreen) return; + + setState(() { + _isFullscreen = fullscreen; + _maxHeight = fullscreen + ? MediaQuery.of(context).size.height + : MediaQuery.of(context).size.height * widget.maxHeightRatio; + }); + + _controller.animateTo( + fullscreen ? 1.0 : 0.0, + duration: const Duration(milliseconds: 350), + curve: Curves.fastOutSlowIn, + ); + } + + void _animateTo(double height) { + if (_isAnimating) return; + _isAnimating = true; + + final value = (height - _minHeight) / (_maxHeight - _minHeight); + _controller.animateTo(value.clamp(0.0, 1.0), + duration: const Duration(milliseconds: 300), + curve: Curves.easeOutBack, + ).whenComplete(() => _isAnimating = false); + } + + Widget _buildDragHandle() { + if (!widget.draggable) return const SizedBox.shrink(); + + return GestureDetector( + behavior: HitTestBehavior.opaque, + onVerticalDragUpdate: _handleDragUpdate, + onVerticalDragEnd: _handleDragEnd, + onDoubleTap: () => _toggleFullscreen(!_isFullscreen), + child: Container( + height: _dragHandleHeight, + alignment: Alignment.center, + child: Container( + width: 48, + height: 4, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor3, + borderRadius: BorderRadius.circular(2), + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + WidgetsBinding.instance.addPostFrameCallback((_) { + // 每次 build 都测量子内容高度,确保内容变化时高度自适应 (拖动时不测量) + if (!_isDragging) { + _measureChildHeight(); + } + }); + + return AnimatedBuilder( + animation: _controller, + builder: (context, _) => RepaintBoundary( + child: Container( + height: _currentHeight, + decoration: BoxDecoration( + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + borderRadius: _isFullscreen + ? null + : BorderRadius.vertical(top: Radius.circular(widget.radius ?? 12)), + ), + child: Column(children: [ + _buildDragHandle(), + buildHeader(context), + SizedBox( + child: _buildContent(), + ), + ]), + ), + ), + ); + } + + Widget _buildContent() => NotificationListener( + onNotification: (notification) { + if (notification is ScrollUpdateNotification) { + final metrics = notification.metrics; + if ((metrics.pixels <= 0 || metrics.pixels >= metrics.maxScrollExtent) && + notification.dragDetails != null) { + _handleDragUpdate(notification.dragDetails!); + } + } + return false; + }, + child: Container( + key: _childKey, + child: widget.child, + ), + ); + + @protected + void _handleDragUpdate(DragUpdateDetails details); + + @protected + void _handleDragEnd(DragEndDetails details); + + @protected + Widget buildHeader(BuildContext context); + + void _baseHandleDragUpdate(DragUpdateDetails details) { + _isDragging = true; + if (_isAnimating || !widget.draggable) return; + + final newHeight = _currentHeight - details.primaryDelta! * 1.2; + _currentHeight = newHeight.clamp(_minHeight, _maxHeight); + _controller.value = (_currentHeight - _minHeight) / (_maxHeight - _minHeight); + } + + void _baseHandleDragEnd(DragEndDetails details) { + final velocity = details.velocity.pixelsPerSecond.dy; + final predictedHeight = _currentHeight + velocity * 0.15; + + if (predictedHeight > _maxHeight * 0.7 || velocity < -800) { + _animateTo(_maxHeight); + } else if (predictedHeight < _minHeight * 1.3 || velocity > 800) { + _animateTo(_minHeight); + } + _isDragging = false; + } +} + +/// 右上角带关闭的底部浮层面板 +class TDPopupBottomDisplayPanel extends TDPopupBasePanel { + const TDPopupBottomDisplayPanel({ + super.key, + required super.child, + super.title, + super.titleColor, + this.titleFontSize, + this.titleLeft = false, + this.hideClose = false, + this.closeColor, + this.closeSize, + this.closeClick, + super.backgroundColor, + super.radius, + super.draggable, + super.maxHeightRatio, + super.minHeightRatio, + }); + /// 标题字体大小 + final double? titleFontSize; + /// 标题是否靠左 + final bool titleLeft; + /// 是否隐藏关闭按钮 + final bool hideClose; + /// 关闭按钮颜色 + final Color? closeColor; + /// 关闭按钮图标尺寸 + final double? closeSize; + /// 关闭按钮点击回调 + final PopupClick? closeClick; + + @override + State createState() => _TDPopupBottomDisplayPanelState(); +} + +class _TDPopupBottomDisplayPanelState extends _TDPopupBaseState { + @override + Widget buildHeader(BuildContext context) { + Widget header = Container( + alignment: widget.titleLeft ? Alignment.centerLeft : Alignment.center, + padding: const EdgeInsets.symmetric(horizontal: 16), + child: TDText( + widget.title ?? '', + textColor: widget.titleColor ?? TDTheme.of(context).fontGyColor1, + font: TDTheme.of(context).fontTitleLarge?.withSize( + widget.titleFontSize?.toInt() ?? + TDTheme.of(context).fontTitleLarge!.size.toInt() + ), + fontWeight: FontWeight.w700, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ); + + if (!widget.hideClose) { + header = Stack( + alignment: Alignment.centerLeft, + children: [ + Padding( + padding: EdgeInsets.only( + right: 40, + left: widget.titleLeft ? 0 : 40 + ), + child: header, + ), + Positioned( + right: 0, + child: IconButton( + icon: Icon(TDIcons.close, + color: widget.closeColor, + size: widget.closeSize, + ), + onPressed: widget.closeClick, + ), + ), + ], + ); + } + + return SizedBox( + height: widget.draggable + ? _TDPopupBaseState._headerHeight - _TDPopupBaseState._dragHandleHeight + : _TDPopupBaseState._headerHeight, + child: header, + ); + } + + @override + void _handleDragUpdate(DragUpdateDetails details) { + super._baseHandleDragUpdate(details); + + final progress = (_currentHeight - _minHeight) / (_maxHeight - _minHeight); + if (progress > 0.85 && !_isFullscreen) { + _toggleFullscreen(true); + } else if (progress < 0.75 && _isFullscreen) { + _toggleFullscreen(false); + } + } + + @override + void _handleDragEnd(DragEndDetails details) => super._baseHandleDragEnd(details); +} + +/// 带确认的底部浮层面板 +class TDPopupBottomConfirmPanel extends TDPopupBasePanel { + const TDPopupBottomConfirmPanel({ + super.key, + required super.child, + super.title, + super.titleColor, + this.leftText, + this.leftTextColor, + this.leftClick, + this.rightText, + this.rightTextColor, + this.rightClick, + this.titleFontSize, + this.leftTextFontSize, + this.rightTextFontSize, + super.backgroundColor, + super.radius, + super.draggable, + super.maxHeightRatio, + super.minHeightRatio, + }); + /// 标题字体大小 + final double? titleFontSize; + /// 左边文本 + final String? leftText; + /// 左边文本字体大小 + final double? leftTextFontSize; + /// 左边文本颜色 + final Color? leftTextColor; + /// 左边文本点击回调 + final PopupClick? leftClick; + /// 右边文本 + final String? rightText; + /// 右边文本字体大小 + final double? rightTextFontSize; + /// 右边文本颜色 + final Color? rightTextColor; + /// 右边文本点击回调 + final PopupClick? rightClick; + + @override + State createState() => _TDPopupBottomConfirmPanelState(); +} + +class _TDPopupBottomConfirmPanelState extends _TDPopupBaseState { + @override + Widget buildHeader(BuildContext context) { + return SizedBox( + height: widget.draggable + ? _TDPopupBaseState._headerHeight - _TDPopupBaseState._dragHandleHeight + : _TDPopupBaseState._headerHeight, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _buildActionButton( + text: widget.leftText ?? context.resource.cancel, + color: widget.leftTextColor ?? TDTheme.of(context).fontGyColor2, + onTap: widget.leftClick, + left: true, + ), + Expanded( + child: Center( + child: TDText( + widget.title ?? '', + textColor: widget.titleColor ?? TDTheme.of(context).fontGyColor1, + font: TDTheme.of(context).fontTitleLarge?.withSize( + widget.titleFontSize?.toInt() ?? + TDTheme.of(context).fontTitleLarge!.size.toInt() + ), + fontWeight: FontWeight.w700, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ), + _buildActionButton( + text: widget.rightText ?? context.resource.confirm, + color: widget.rightTextColor ?? TDTheme.of(context).brandNormalColor, + onTap: widget.rightClick, + left: false, + ), + ], + ), + ); + } + + Widget _buildActionButton({ + required String text, + required Color color, + required VoidCallback? onTap, + required bool left, + }) => GestureDetector( + onTap: onTap, + child: Padding( + padding: EdgeInsets.only( + left: left ? 16 : 0, + right: left ? 0 : 16, + ), + child: TDText( + text, + textColor: color, + font: (left + ? TDTheme.of(context).fontBodyLarge + : TDTheme.of(context).fontTitleMedium + )?.withSize( + left + ? widget.leftTextFontSize?.toInt() ?? TDTheme.of(context).fontBodyLarge!.size.toInt() + : widget.rightTextFontSize?.toInt() ?? TDTheme.of(context).fontTitleMedium!.size.toInt() + ), + fontWeight: left ? FontWeight.w400 : FontWeight.w600, + ), + ), + ); + + @override + void _handleDragUpdate(DragUpdateDetails details) { + super._baseHandleDragUpdate(details); + + const threshold = 0.15; + final progress = (_currentHeight - _minHeight) / (_maxHeight - _minHeight); + if (progress > (1 - threshold) && !_isFullscreen) { + _toggleFullscreen(true); + } else if (progress < threshold && _isFullscreen) { + _toggleFullscreen(false); + } + } + + @override + void _handleDragEnd(DragEndDetails details) => super._baseHandleDragEnd(details); +} + +/// 居中浮层面板 +class TDPopupCenterPanel extends StatelessWidget { + const TDPopupCenterPanel({ + super.key, + required this.child, + this.closeUnderBottom = false, + this.closeColor, + this.closeClick, + this.backgroundColor, + this.radius, + this.closeSize, + }); + + /// 子控件 + final Widget child; + /// 关闭按钮是否在视图框下方 + final bool closeUnderBottom; + /// 关闭按钮颜色 + final Color? closeColor; + /// 关闭按钮图标尺寸 + final double? closeSize; + /// 关闭按钮点击回调 + final PopupClick? closeClick; + /// 背景颜色 + final Color? backgroundColor; + /// 圆角 + final double? radius; + + @override + Widget build(BuildContext context) { + if (closeUnderBottom) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 40), + Container( + margin: const EdgeInsets.symmetric(vertical: 24), + decoration: BoxDecoration( + color: backgroundColor ?? TDTheme.of(context).whiteColor1, + borderRadius: BorderRadius.circular(radius ?? 12), + ), + child: child, + ), + IconButton( + icon: Icon(TDIcons.close_circle, + color: closeColor ?? TDTheme.of(context).fontWhColor1, + size: closeSize, + ), + onPressed: closeClick, + ), + ], + ); + } + + return Container( + decoration: BoxDecoration( + color: backgroundColor ?? TDTheme.of(context).whiteColor1, + borderRadius: BorderRadius.circular(radius ?? 12), + ), + child: Stack( + children: [ + child, + Positioned( + top: TDTheme.of(context).spacer8, + right: TDTheme.of(context).spacer8, + child: IconButton( + icon: Icon(TDIcons.close, + color: closeColor, + size: closeSize, + ), + onPressed: closeClick, + ), + ), + ], + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/popup/td_popup_route.dart b/tdesign-component/lib/src/components/popup/td_popup_route.dart new file mode 100644 index 000000000..890a51a34 --- /dev/null +++ b/tdesign-component/lib/src/components/popup/td_popup_route.dart @@ -0,0 +1,301 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +const Duration _bottomSheetEnterDuration = Duration(milliseconds: 250); +const Duration _bottomSheetExitDuration = Duration(milliseconds: 200); + +/// 从屏幕弹出的方向 +enum SlideTransitionFrom { top, right, left, bottom, center } + +/// 从屏幕的某个方向滑动弹出的Dialog框的路由,比如从顶部、底部、左、右滑出页面 +class TDSlidePopupRoute extends PopupRoute { + TDSlidePopupRoute({ + required this.builder, + this.barrierLabel, + this.modalBarrierColor = Colors.black54, + this.isDismissible = true, + this.modalBarrierFull = false, + this.slideTransitionFrom = SlideTransitionFrom.bottom, + this.modalWidth, + this.modalHeight, + this.modalTop = 0, + this.modalLeft = 0, + this.open, + this.opened, + this.close, + this.barrierClick, + this.focusMove = false, + }); + + /// 控件构建器 + final WidgetBuilder builder; + + /// 蒙层颜色 + final Color? modalBarrierColor; + + /// 点击蒙层能否关闭 + final bool isDismissible; + + /// 是否全屏显示蒙层 + final bool modalBarrierFull; + + /// 设置从屏幕的哪个方向滑出 + final SlideTransitionFrom slideTransitionFrom; + + /// 弹出框宽度 + final double? modalWidth; + + /// 弹出框高度 + final double? modalHeight; + + /// 弹出框顶部距离 + final double? modalTop; + + /// 弹出框左侧距离 + final double? modalLeft; + + /// 打开前事件 + final VoidCallback? open; + + /// 打开后事件 + final VoidCallback? opened; + + /// 关闭前事件 + final VoidCallback? close; + + /// 蒙层点击事件,仅在[modalBarrierFull]为false时触发 + final VoidCallback? barrierClick; + + /// 是否有输入框获取焦点时整体平移避免输入框被遮挡 + final bool focusMove; + + Color get _barrierColor => modalBarrierColor ?? Colors.black54; + + @override + Duration get transitionDuration => _bottomSheetEnterDuration; + + @override + Duration get reverseTransitionDuration => _bottomSheetExitDuration; + + @override + bool get barrierDismissible => isDismissible; + + @override + final String? barrierLabel; + + @override + Color get barrierColor => modalBarrierFull ? _barrierColor : Colors.transparent; + + /// 键盘焦点对象的Y坐标 + var _focusY = 0.0; + + /// 键盘焦点对象的高度 + var _focusHeight = 0.0; + + /// 键盘出现后bottom的偏移量 + var _lastBottom = 0.0; + + // 实现转场动画 + @override + Widget buildTransitions( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + var animValue = decelerateEasing.transform(animation.value); + return Stack( + children: [ + if (!modalBarrierFull) + _getPositionWidget( + context, + IgnorePointer( + ignoring: true, + child: Container( + color: _barrierColor.withAlpha((animValue * _barrierColor.alpha).toInt()), + child: GestureDetector( + onTap: () { + barrierClick?.call(); + if (isDismissible) { + Navigator.pop(context); + } + }, + onDoubleTap: () { + barrierClick?.call(); + if (isDismissible) { + Navigator.pop(context); + } + }, + ), + ), + ), + ), + _getPositionWidget( + context, + Align( + alignment: slideTransitionFromToAlignment(slideTransitionFrom), + child: slideTransitionFrom != SlideTransitionFrom.center + ? FractionalTranslation( + translation: _getOffset(animValue, slideTransitionFrom), + child: ClipRect( + clipper: RectClipper(animValue, slideTransitionFrom), + child: child, + ), + ) + : Transform( + transform: Matrix4.diagonal3Values(animValue, animValue, 1), + alignment: Alignment.center, + child: child, + ), + ), + ), + ], + ); + } + + @override + Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { + return Material( + child: builder.call(context), + color: Colors.transparent, + ); + } + + @override + TickerFuture didPush() { + startFocusListener(navigator!.context); + open?.call(); + animation?.addStatusListener((status) { + if (status == AnimationStatus.completed) { + opened?.call(); + } + }); + return super.didPush(); + } + + @override + void dispose() { + close?.call(); + stopFocusListener(navigator!.context); + super.dispose(); + } + + /// 监听焦点变化 + void startFocusListener(BuildContext context) { + FocusManager.instance.addListener(_handleFocusChange); + } + + /// 停止监听焦点变化 + void stopFocusListener(BuildContext context) { + FocusManager.instance.removeListener(_handleFocusChange); + } + + void _handleFocusChange() { + // 获取当前的焦点节点 + var focusNode = FocusManager.instance.primaryFocus; + if (focusNode != null && focusNode.context != null) { + var renderObject = focusNode.context!.findRenderObject(); + if (renderObject is RenderPointerListener) { + _focusY = renderObject.localToGlobal(Offset.zero).dy; + _focusHeight = renderObject.size.height; + } + } + (focusNode?.context as Element?)?.markNeedsBuild(); + } + + @override + bool didPop(T? result) { + close?.call(); + return super.didPop(result); + } + + Widget _getPositionWidget(BuildContext context, Widget child) { + var bottom = 0.0; + var mediaQuery = MediaQuery.of(context); + if (slideTransitionFrom == SlideTransitionFrom.bottom) { + bottom = mediaQuery.viewInsets.bottom; + } else { + if ((_focusY + mediaQuery.viewInsets.bottom + _focusHeight) > mediaQuery.size.height) { + bottom = -(mediaQuery.size.height - (_focusY + mediaQuery.viewInsets.bottom + _focusHeight + 10)); + _lastBottom = bottom; + } else { + if (_lastBottom > 0.0) { + bottom = max((_lastBottom -= 5), 0).toDouble(); + } + } + } + + var screenSize = mediaQuery.size; + var _modalTop = (modalTop ?? 0).clamp(0, screenSize.height).toDouble() - (focusMove ? bottom : 0); + var _modalLeft = (modalLeft ?? 0).clamp(0, screenSize.width).toDouble(); + var _modalHeight = (modalHeight ?? screenSize.height).clamp(0, screenSize.height - _modalTop).toDouble(); + var _modalWidth = (modalWidth ?? screenSize.width).clamp(0, screenSize.width - _modalLeft).toDouble(); + return Positioned( + top: _modalTop, + bottom: screenSize.height - _modalTop - _modalHeight, + left: _modalLeft, + right: screenSize.width - _modalLeft - _modalWidth, + child: child, + ); + } + + Offset _getOffset(double animValue, SlideTransitionFrom slideTransitionFrom) { + switch (slideTransitionFrom) { + case SlideTransitionFrom.top: + return Offset(0, animValue - 1); + case SlideTransitionFrom.right: + return Offset(1 - animValue, 0); + case SlideTransitionFrom.left: + return Offset(animValue - 1, 0); + case SlideTransitionFrom.bottom: + return Offset(0, 1 - animValue); + default: + return const Offset(0, 0); + } + } +} + +Alignment slideTransitionFromToAlignment(SlideTransitionFrom from) { + switch (from) { + case SlideTransitionFrom.top: + return Alignment.topCenter; + case SlideTransitionFrom.right: + return Alignment.centerRight; + case SlideTransitionFrom.left: + return Alignment.centerLeft; + case SlideTransitionFrom.bottom: + return Alignment.bottomCenter; + case SlideTransitionFrom.center: + return Alignment.center; + } +} + +class RectClipper extends CustomClipper { + final double animValue; + final SlideTransitionFrom slideTransitionFrom; + + RectClipper(this.animValue, this.slideTransitionFrom); + + @override + Rect getClip(Size size) { + switch (slideTransitionFrom) { + case SlideTransitionFrom.top: + return Rect.fromLTWH(0, size.height * (1 - animValue), size.width, size.height); + case SlideTransitionFrom.right: + return Rect.fromLTWH(0, 0, size.width * animValue, size.height); + case SlideTransitionFrom.left: + return Rect.fromLTWH(size.width * (1 - animValue), 0, size.width, size.height); + case SlideTransitionFrom.bottom: + return Rect.fromLTWH(0, 0, size.width, size.height * animValue); + default: + return Rect.fromLTWH(0, 0, size.width, size.height); + } + } + + @override + bool shouldReclip(covariant CustomClipper oldClipper) { + return oldClipper != this; + } +} diff --git a/tdesign-component/lib/src/components/progress/td_progress.dart b/tdesign-component/lib/src/components/progress/td_progress.dart new file mode 100644 index 000000000..eb32f4c61 --- /dev/null +++ b/tdesign-component/lib/src/components/progress/td_progress.dart @@ -0,0 +1,656 @@ +import 'package:flutter/material.dart'; +import './td_progress_circular.dart'; +import '../../../tdesign_flutter.dart'; + +enum TDProgressType { linear, circular, micro, button } + +enum TDProgressLabelPosition { inside, left, right } + +enum TDProgressStatus { primary, warning, danger, success } + +abstract class TDLabelWidget extends Widget { + const TDLabelWidget({super.key}); +} + +class TDTextLabel extends Text implements TDLabelWidget { + const TDTextLabel(String data, {Key? key, TextStyle? style}) + : super(data, key: key, style: style); +} + +class TDIconLabel extends Icon implements TDLabelWidget { + const TDIconLabel(IconData icon, {Key? key, double? size, Color? color}) + : super(icon, key: key, size: size, color: color); +} + +class TDProgress extends StatelessWidget { + TDProgress({ + Key? key, + required this.type, + double? value, + this.label, + this.progressStatus = TDProgressStatus.primary, + this.progressLabelPosition = TDProgressLabelPosition.inside, + double? strokeWidth, + this.color, + this.backgroundColor, + this.linearBorderRadius, + double? circleRadius, + this.showLabel = true, + this.customProgressLabel, + this.labelWidgetWidth, + this.labelWidgetAlignment, + this.onTap, + this.onLongPress, + int animationDuration = 300, + }) : value = _validateProgress(value), + strokeWidth = _validatePositiveDouble(strokeWidth), + circleRadius = _validatePositiveDouble(circleRadius), + animationDuration = _validatePositiveInt(animationDuration), + super(key: key); + + /// 进度条类型 + final TDProgressType type; + + /// 进度值 (0.0 到 1.0 之间的正数) + final double? value; + + /// 进度条标签 + final TDLabelWidget? label; + + /// 进度条状态 + final TDProgressStatus progressStatus; + + /// 标签显示位置 + final TDProgressLabelPosition progressLabelPosition; + + /// 进度条粗细 (正数) + final double? strokeWidth; + + /// 进度条颜色 + final Color? color; + + /// 进度条背景颜色 + final Color? backgroundColor; + + /// 条形进度条末端形状 + final BorderRadiusGeometry? linearBorderRadius; + + /// 环形进度条半径 (正数) + final double? circleRadius; + + /// 是否显示标签 + final bool showLabel; + + /// 自定义标签 + final Widget? customProgressLabel; + + /// 自定义标签宽度 + final double? labelWidgetWidth; + + /// 自定义标签对齐方式 + final Alignment? labelWidgetAlignment; + + /// 点击事件 + final VoidCallback? onTap; + + /// 长按事件 + final VoidCallback? onLongPress; + + /// 动画持续时间 (正整数,单位为毫秒) + final int? animationDuration; + + static double? _validateProgress(double? value) => + value == null ? 0 : value.clamp(0.0, 1.0); + + static double? _validatePositiveDouble(double? value) => value == null + ? null + : value <= 0 + ? 0 + : value; + + static int _validatePositiveInt(int? value) => + value == null || value <= 0 ? 0 : value; + + @override + Widget build(BuildContext context) { + final defaultValues = _getDefaultValues(context, type); + return ProgressIndicator( + value: value, + label: label, + progressStatus: progressStatus, + progressLabelPosition: progressLabelPosition, + strokeWidth: strokeWidth ?? defaultValues.strokeWidth, + color: color, + backgroundColor: backgroundColor ?? defaultValues.backgroundColor, + linearBorderRadius: linearBorderRadius ?? defaultValues.linearBorderRadius, + circleRadius: circleRadius ?? defaultValues.circleRadius, + showLabel: showLabel, + customProgressLabel:customProgressLabel, + labelWidgetWidth: labelWidgetWidth, + labelWidgetAlignment: labelWidgetAlignment, + onTap: onTap, + onLongPress: onLongPress, + type: type, + context: context, + ); + } + + _DefaultValues _getDefaultValues(BuildContext context, TDProgressType type) { + switch (type) { + case TDProgressType.linear: + return _DefaultValues( + strokeWidth: 20.0, + backgroundColor: TDTheme.of(context).grayColor3, + linearBorderRadius: BorderRadius.circular(20), + circleRadius: 0, + ); + case TDProgressType.circular: + return _DefaultValues( + strokeWidth: 5.0, + backgroundColor: TDTheme.of(context).grayColor2, + linearBorderRadius: BorderRadius.circular(20), + circleRadius: 100.0, + ); + case TDProgressType.micro: + return _DefaultValues( + strokeWidth: 2.0, + backgroundColor: TDTheme.of(context).grayColor2, + linearBorderRadius: BorderRadius.circular(20), + circleRadius: 25.0, + ); + case TDProgressType.button: + return _DefaultValues( + strokeWidth: 50.0, + backgroundColor: TDTheme.of(context).brandNormalColor, + linearBorderRadius: BorderRadius.circular(8), + circleRadius: 0, + ); + } + } +} + +class _DefaultValues { + final double strokeWidth; + final Color backgroundColor; + final BorderRadiusGeometry linearBorderRadius; + final double circleRadius; + + _DefaultValues({ + required this.strokeWidth, + required this.backgroundColor, + required this.linearBorderRadius, + required this.circleRadius, + }); +} + +class ProgressIndicator extends StatefulWidget { + final double? value; + final TDLabelWidget? label; + final TDProgressLabelPosition progressLabelPosition; + final double strokeWidth; + final double circleRadius; + final BorderRadiusGeometry linearBorderRadius; + final Color? color; + final Color backgroundColor; + final TDProgressType type; + final TDProgressStatus progressStatus; + final bool showLabel; + final Widget? customProgressLabel; + final double? labelWidgetWidth; + final Alignment? labelWidgetAlignment; + final VoidCallback? onTap; + final VoidCallback? onLongPress; + final int animationDuration; + final BuildContext? context; + + const ProgressIndicator({ + Key? key, + this.value, + this.label, + this.progressLabelPosition = TDProgressLabelPosition.inside, + required this.strokeWidth, + required this.linearBorderRadius, + required this.circleRadius, + this.color, + required this.backgroundColor, + required this.type, + this.progressStatus = TDProgressStatus.primary, + this.showLabel = true, + this.customProgressLabel, + this.labelWidgetWidth, + this.labelWidgetAlignment, + this.onTap, + this.onLongPress, + this.animationDuration = 300, + this.context, + }) : super(key: key); + + @override + _ProgressIndicatorState createState() => _ProgressIndicatorState(); +} + +class _ProgressIndicatorState extends State + with SingleTickerProviderStateMixin { + late AnimationController _animationController; + late Animation _animation; + late Color _effectiveColor; + late Widget _effectiveLabel; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + vsync: this, + duration: Duration(milliseconds: widget.animationDuration)); + _updateAnimation(); + _updateEffectiveColor(); + _updateEffectiveLabel(); + } + + @override + void didUpdateWidget(ProgressIndicator oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.value != widget.value) { + _updateAnimation(oldWidgetValue: oldWidget.value); + } + if (oldWidget.color != widget.color || + oldWidget.progressStatus != widget.progressStatus) { + _updateEffectiveColor(); + } + if (oldWidget.label != widget.label || + oldWidget.progressStatus != widget.progressStatus) { + _updateEffectiveLabel(); + } + } + + void _updateEffectiveColor() { + _effectiveColor = + widget.color ?? _getColorFromStatus(widget.progressStatus); + } + + void _updateEffectiveLabel() { + _effectiveLabel = + widget.label ?? _getDefaultLabelFromStatus(widget.progressStatus); + } + + void _updateAnimation({double? oldWidgetValue}) { + _animation = Tween( + begin: oldWidgetValue ?? _animationController.value, + end: widget.value) + .animate(_animationController); + _animationController.forward(from: 0); + } + + Widget _getDefaultLabelFromStatus(TDProgressStatus status) { + final showAutoText = widget.value != null; + final showInsideLabel = + widget.progressLabelPosition == TDProgressLabelPosition.inside && + widget.type != TDProgressType.circular; + final showIconBorder = widget.type == TDProgressType.linear; + + Widget getAutoText() => showAutoText && widget.type != TDProgressType.micro + ? Text('${(widget.value! * 100).round()}%') + : const Text(''); + + final statusWidgets = { + TDProgressStatus.primary: getAutoText(), + TDProgressStatus.warning: showInsideLabel + ? getAutoText() + : showIconBorder + ? const Icon(TDIcons.error_circle_filled) + : const Icon(TDIcons.error), + TDProgressStatus.danger: showInsideLabel + ? getAutoText() + : showIconBorder + ? const Icon(TDIcons.close_circle_filled) + : const Icon(TDIcons.close), + TDProgressStatus.success: showInsideLabel + ? getAutoText() + : showIconBorder + ? const Icon(TDIcons.check_circle_filled) + : const Icon(TDIcons.check), + }; + + return statusWidgets[status] ?? getAutoText(); + } + + Color _getColorFromStatus(TDProgressStatus status) { + switch (status) { + case TDProgressStatus.primary: + return TDTheme.of(widget.context).brandNormalColor; + case TDProgressStatus.warning: + return TDTheme.of(widget.context).warningNormalColor; + case TDProgressStatus.danger: + return TDTheme.of(widget.context).errorNormalColor; + case TDProgressStatus.success: + return TDTheme.of(widget.context).successNormalColor; + } + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.type == TDProgressType.linear) + _buildLinearProgress() + else if (widget.type == TDProgressType.circular) + _buildCircularProgress() + else if (widget.type == TDProgressType.micro) + _buildMicroProgress() + else if (widget.type == TDProgressType.button) + _buildButtonProgress() + ], + ); + } + + Widget _buildLinearProgress() { + return LayoutBuilder( + builder: (context, constraints) { + final maxWidth = constraints.maxWidth; + + if (widget.value != null && + widget.progressLabelPosition == TDProgressLabelPosition.inside) { + return _buildInsideLabel(maxWidth); + } else { + return _buildOutsideLabel(maxWidth); + } + }, + ); + } + + Widget _buildInsideLabel(double maxWidth) { + return AnimatedBuilder( + animation: _animation, + builder: (context, child) { + final progressWidth = _animation.value * maxWidth; + return Stack( + children: [ + _buildBackgroundContainer(), + if (widget.value! > 0.1) + _buildProgressContainerWithLabel(progressWidth) + else + _buildProgressContainerWithLabelOutside(progressWidth), + ], + ); + }, + ); + } + + Widget _buildOutsideLabel(double maxWidth) { + return AnimatedBuilder( + animation: _animation, + builder: (context, child) { + final progressWidth = _animation.value * maxWidth; + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (widget.progressLabelPosition == TDProgressLabelPosition.left) + Container( + padding: const EdgeInsets.only(right: 8.0), + alignment:widget.labelWidgetAlignment??Alignment.centerRight, + constraints: BoxConstraints( + minWidth:widget.labelWidgetWidth??(maxWidth*0.1>70?maxWidth*0.04 + :maxWidth*0.1), + ), + child:widget.customProgressLabel?? + _buildLabelWidget(TDTheme.of(context).fontGyColor1), + ), + Expanded( + child: Stack( + children: [ + _buildBackgroundContainer(), + Container( + height: widget.strokeWidth, + width: progressWidth, + decoration: BoxDecoration( + color: _effectiveColor, + borderRadius: widget.linearBorderRadius, + ), + ) + ], + )), + if (widget.progressLabelPosition == TDProgressLabelPosition.right) + Container( + padding: const EdgeInsets.only(left: 8.0), + alignment:widget.labelWidgetAlignment??Alignment.centerLeft, + constraints: BoxConstraints( + minWidth:widget.labelWidgetWidth??(maxWidth*0.1>70?maxWidth*0.04 + :maxWidth*0.1), + ), + child:widget.customProgressLabel?? + _buildLabelWidget(TDTheme.of(context).fontGyColor1), + ) + ], + ); + }, + ); + } + + Widget _buildBackgroundContainer() { + return Container( + height: widget.strokeWidth, + decoration: BoxDecoration( + borderRadius: widget.linearBorderRadius, + color: widget.backgroundColor, + ), + ); + } + + Widget _buildProgressContainerWithLabel(double progressWidth) { + return Container( + height: widget.strokeWidth, + width: progressWidth, + decoration: BoxDecoration( + color: _effectiveColor, + borderRadius: widget.linearBorderRadius, + ), + child: widget.showLabel + ? Align( + alignment: Alignment.centerRight, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: _buildLabelWidget(TDTheme.of(context).fontWhColor1), + ), + ) + : null, + ); + } + + Widget _buildProgressContainerWithLabelOutside(double progressWidth) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + height: widget.strokeWidth, + width: progressWidth, + decoration: BoxDecoration( + color: _effectiveColor, + borderRadius: BorderRadius.only( + topLeft: widget.linearBorderRadius.resolve(TextDirection.ltr).topLeft, + bottomLeft: + widget.linearBorderRadius.resolve(TextDirection.ltr).bottomLeft, + topRight: Radius.circular( + widget.linearBorderRadius.resolve(TextDirection.ltr).topRight.x / + 2), + bottomRight: Radius.circular( + widget.linearBorderRadius.resolve(TextDirection.ltr).bottomRight.x / + 2), + ), + ), + ), + if (widget.showLabel) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: _buildLabelWidget(TDTheme.of(context).fontGyColor1), + ), + ], + ); + } + + Widget _buildLabelWidget(Color labelColor) { + late double iconSize; + late double fontSize; + late FontWeight fontWeight; + + switch (widget.type) { + case TDProgressType.linear: + if (widget.progressLabelPosition != TDProgressLabelPosition.inside) { + fontSize = widget.strokeWidth > 14 ? widget.strokeWidth : 14; + iconSize = widget.strokeWidth > 20 ? widget.strokeWidth : 20; + } else { + fontSize = widget.strokeWidth * 0.6; + iconSize = widget.strokeWidth; + } + fontWeight = + _animation.value <= 0.1 ? FontWeight.bold : FontWeight.normal; + break; + case TDProgressType.circular: + iconSize = widget.circleRadius * 0.4; + fontSize = widget.circleRadius * 0.15; + fontWeight = FontWeight.bold; + break; + case TDProgressType.micro: + iconSize = widget.circleRadius * 0.5; + fontSize = widget.circleRadius * 0.2; + fontWeight = FontWeight.normal; + break; + case TDProgressType.button: + iconSize = widget.strokeWidth * 0.3; + fontSize = widget.strokeWidth * 0.3; + fontWeight = FontWeight.normal; + break; + } + + return IconTheme( + data: IconThemeData(color: _effectiveColor, size: iconSize), + child: DefaultTextStyle( + style: TextStyle( + color: labelColor, + fontSize: fontSize, + fontWeight: fontWeight, + ), + child: _effectiveLabel, + ), + ); + } + + Widget _buildCircularProgress() { + return AnimatedBuilder( + animation: _animation, + builder: (context, child) { + return Stack( + alignment: Alignment.center, + children: [ + SizedBox( + height: widget.circleRadius, + width: widget.circleRadius, + child: Padding( + padding: EdgeInsets.all(widget.strokeWidth / 2), + child: TDProgressCircular( + strokeWidth: widget.strokeWidth, + circleRadius: widget.circleRadius, + value: _animation.value, + backgroundColor: widget.backgroundColor, + valueColor: AlwaysStoppedAnimation(_effectiveColor), + ), + ), + ), + if (widget.showLabel) + _buildLabelWidget(TDTheme.of(widget.context).fontGyColor1), + ], + ); + }, + ); + } + + Widget _buildMicroProgress() { + return AnimatedBuilder( + animation: _animation, + builder: (context, child) { + return GestureDetector( + onTap: widget.onTap, + onLongPress: widget.onLongPress, + child: Stack( + alignment: Alignment.center, + children: [ + _buildMicroOutline(), + if (widget.showLabel) + _buildLabelWidget(TDTheme.of(widget.context).fontGyColor1), + ], + )); + }); + } + + Widget _buildMicroOutline() { + return SizedBox( + height: widget.circleRadius, + width: widget.circleRadius, + child: Padding( + padding: EdgeInsets.all(widget.strokeWidth / 2), + child: TDProgressCircular( + strokeWidth: widget.strokeWidth, + circleRadius: widget.circleRadius, + value: _animation.value, + backgroundColor: widget.backgroundColor, + valueColor: AlwaysStoppedAnimation(_effectiveColor), + ), + ), + ); + } + + Widget _buildButtonProgress() { + return LayoutBuilder( + builder: (context, constraints) { + final maxWidth = constraints.maxWidth; + return AnimatedBuilder( + animation: _animation, + builder: (context, child) { + final progressWidth = maxWidth * _animation.value; + return ClipRRect( + borderRadius: widget.linearBorderRadius, + child: GestureDetector( + onTap: widget.onTap, + onLongPress: widget.onLongPress, + child: Stack( + children: [ + _buildBackgroundContainer(), + _buildButtonActiveContainer(progressWidth), + if (widget.showLabel) _buildButtonLabel(maxWidth), + ], + )), + ); + }); + }, + ); + } + + Widget _buildButtonActiveContainer(double progressWidth) { + return Container( + height: widget.strokeWidth, + width: progressWidth, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + _effectiveColor, + TDTheme.of(widget.context).brandDisabledColor.withOpacity(.5) + ], + ), + ), + ); + } + + Widget _buildButtonLabel(double maxWidth) { + return Container( + height: widget.strokeWidth, + alignment: Alignment.center, + child: _buildLabelWidget(TDTheme.of(widget.context).fontWhColor1), + ); + } +} diff --git a/tdesign-component/lib/src/components/progress/td_progress_circular.dart b/tdesign-component/lib/src/components/progress/td_progress_circular.dart new file mode 100644 index 000000000..9dd5e1d39 --- /dev/null +++ b/tdesign-component/lib/src/components/progress/td_progress_circular.dart @@ -0,0 +1,90 @@ +import 'dart:math' as math; + +import 'package:flutter/material.dart'; + +/// 私有组件类,用于展示环形进度条 +class TDProgressCircular extends StatelessWidget { + final double _value; + final double strokeWidth; + final Color backgroundColor; + final Animation valueColor; + final double circleRadius; + + const TDProgressCircular({ + Key? key, + required double value, + required this.strokeWidth, + required this.backgroundColor, + required this.valueColor, + required this.circleRadius, + }) : _value = value, + super(key: key); + + double get value => _value.clamp(0.0, 1.0); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: circleRadius * 2, + height: circleRadius * 2, + child: CustomPaint( + painter: _TDProgressCircularPainter( + value: value, + backgroundColor: backgroundColor, + valueColor: valueColor, + circleRadius: circleRadius, + strokeWidth: strokeWidth, + ), + ), + ); + } +} + +class _TDProgressCircularPainter extends CustomPainter { + final double value; + final Color backgroundColor; + final Animation valueColor; + final double circleRadius; + final double strokeWidth; + + _TDProgressCircularPainter({ + required this.value, + required this.backgroundColor, + required this.valueColor, + required this.circleRadius, + required this.strokeWidth, + }); + + @override + void paint(Canvas canvas, Size size) { + final center = size.width / 2; + final radius = center - strokeWidth / 2; + + final backgroundPaint = Paint() + ..color = backgroundColor + ..style = PaintingStyle.stroke + ..strokeWidth = strokeWidth; + + final valuePaint = Paint() + ..color = valueColor.value + ..style = PaintingStyle.stroke + ..strokeWidth = strokeWidth + ..strokeCap = StrokeCap.round; + + canvas.drawCircle(Offset(center, center), radius, backgroundPaint); + + final sweepAngle = 2 * math.pi * value; + canvas.drawArc( + Rect.fromCircle(center: Offset(center, center), radius: radius), + -math.pi / 2, + sweepAngle, + false, + valuePaint, + ); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} diff --git a/tdesign-component/lib/src/components/radio/td_radio.dart b/tdesign-component/lib/src/components/radio/td_radio.dart new file mode 100644 index 000000000..ceb4caea3 --- /dev/null +++ b/tdesign-component/lib/src/components/radio/td_radio.dart @@ -0,0 +1,379 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/auto_size.dart'; + +enum TDRadioStyle { + circle, // 圆形 + square, // 方形 + check, // 对号样式 + hollowCircle, // 镂空圆点样式 +} + +/// 单选框按钮,继承自TDCheckbox,字段含义与父类一致 +class TDRadio extends TDCheckbox { + /// 单选框按钮样式 + final TDRadioStyle radioStyle; + + const TDRadio({ + String? id, + Key? key, + String? title, + Font? titleFont, + String? subTitle, + Font? subTitleFont, + bool enable = true, + int subTitleMaxLine = 1, + int titleMaxLine = 1, + Color? selectColor, + Color? disableColor, + ContentBuilder? customContentBuilder, + double? spacing, + bool? cardMode, + bool? showDivider, + TDCheckBoxSize size = TDCheckBoxSize.small, + this.radioStyle = TDRadioStyle.circle, + TDContentDirection contentDirection = TDContentDirection.right, + IconBuilder? customIconBuilder, + Color? titleColor, + Color? subTitleColor, + Color? backgroundColor, + double? checkBoxLeftSpace, + double? insetSpacing, + EdgeInsetsGeometry? customSpace, + }) : super( + id: id, + key: key, + title: title, + subTitle: subTitle, + titleFont: titleFont, + subTitleFont: subTitleFont, + subTitleMaxLine: subTitleMaxLine, + enable: enable, + size: size, + cardMode: cardMode ?? false, + showDivider: showDivider ?? true, + titleMaxLine: titleMaxLine, + customContentBuilder: customContentBuilder, + contentDirection: contentDirection, + spacing: spacing, + customIconBuilder: customIconBuilder, + selectColor: selectColor, + disableColor: disableColor, + titleColor: titleColor, + subTitleColor: subTitleColor, + backgroundColor: backgroundColor, + checkBoxLeftSpace: checkBoxLeftSpace, + insetSpacing: insetSpacing, + customSpace: customSpace); + + @override + Widget buildDefaultIcon(BuildContext context, TDCheckboxGroupState? groupState, bool isSelected) { + if (cardMode == true) { + return Container(); + } + TDRadioStyle? style; + if (groupState is TDRadioGroupState) { + style = (groupState.widget as TDRadioGroup).radioCheckStyle; + } + + style = style ?? radioStyle; + + var size = 24.0; + final theme = TDTheme.of(context); + + // 由于镂空圆没有现成icon,因而自己画一个` + if (style == TDRadioStyle.hollowCircle) { + return SizedBox( + width: size, + height: size, + child: CustomPaint( + painter: HollowCircle(!enable + ? (isSelected ? theme.brandDisabledColor : theme.grayColor4) + : isSelected + ? selectColor ?? theme.brandNormalColor + : theme.grayColor4), + ), + ); + } + + IconData? iconData; + switch (style) { + case TDRadioStyle.check: + iconData = isSelected ? TDIcons.check : null; + break; + case TDRadioStyle.square: + iconData = isSelected ? TDIcons.check_rectangle_filled : TDIcons.rectangle; + break; + default: + iconData = isSelected ? TDIcons.check_circle_filled : TDIcons.circle; + break; + } + if (iconData != null) { + return Icon(iconData, + size: size, + color: !enable + ? (isSelected ? (disableColor ?? theme.brandDisabledColor) : theme.grayColor4) + : isSelected + ? selectColor ?? theme.brandNormalColor + : theme.grayColor4); + } else { + return SizedBox( + width: size, + height: size, + ); + } + } + + @override + State createState() { + return TDRadioState(); + } +} + +class TDRadioState extends TDCheckboxState { + @override + Widget build(BuildContext context) { + // 检查是否包含在FuiCheckBoxGroup内,如果是的话,状态由Group管理 + final groupState = TDCheckboxGroupInherited.of(context)?.state; + if (groupState is TDRadioGroupState) { + final strictMode = (groupState.widget as TDRadioGroup).strictMode; + // 严格模式下不能取消选项,只能切换 + if (strictMode == true) { + canNotCancel = true; + } + } + return super.build(context); + } +} + +class HollowCircle extends CustomPainter { + HollowCircle(this.color); + + // 绘制颜色 + final Color color; + + @override + void paint(Canvas canvas, Size size) { + var paint = Paint() + ..color = color + ..strokeWidth = 1.5 + ..style = PaintingStyle.stroke; + canvas.drawCircle(const Offset(10.5, 10.5), 10.5, paint); + paint.style = PaintingStyle.fill; + canvas.drawCircle(const Offset(10.5, 10.5), 6, paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} + +/// RadioGroup分组对象,继承自TDCheckboxGroup,字段含义与父类一致 +/// RadioGroup应该嵌套在RadioGroup内,所有在RadioGroup的RadioButton只能有一个被选中 +/// +/// cardMode: 使用卡片样式,需要配合direction 和 directionalTdRadios 使用, +/// 组合为横向、纵向卡片,同时需要在每个TDRadio上设置cardMode参数。 +class TDRadioGroup extends TDCheckboxGroup { + /// 严格模式下,用户不能取消勾选,只能切换选择项, + final bool strictMode; + + /// 勾选样式 + final TDRadioStyle? radioCheckStyle; + + /// 是否显示下划线 + final bool showDivider; + + /// 自定义下划线 + final Widget? divider; + + ///每行几列 + final int rowCount; + + TDRadioGroup( + {Key? key, + Widget? child, // 使用child 则请勿设置direction + Axis? direction, // direction 对 directionalTdRadios 起作用 + List? directionalTdRadios, + String? selectId, // 默认选择项的id + bool? passThrough, // 非通栏单选样式 用于使用child 或 direction == Axis.vertical 场景 + bool cardMode = false, + this.strictMode = true, + this.radioCheckStyle, + int? titleMaxLine, // item的行数 + IconBuilder? customIconBuilder, + ContentBuilder? customContentBuilder, + double? spacing, // icon和文字距离 + this.rowCount = 1, + TDContentDirection? contentDirection, + OnRadioGroupChange? onRadioGroupChange, // 切换监听 + this.showDivider = false, + this.divider, + + /// 可以通过控制器操作勾选状态 + TDCheckboxGroupController? controller}) + : assert(() { + // 使用direction属性则必须配合directionalTdRadios,child字段无效 + if (direction != null && directionalTdRadios == null) { + throw FlutterError('[TDRadioGroup] direction and directionalTdRadios must set at the same time'); + } + // 未使用direction则必须设置child + if (direction == null && child == null) { + throw FlutterError('[TDRadioGroup] direction means use child as the exact one, but child is null'); + } + // 横向单选框 每个选项有字数限制 + if (direction == Axis.horizontal && directionalTdRadios != null) { + directionalTdRadios.forEach((element) { + if (element.subTitle != null) { + throw FlutterError('horizontal radios style should not have subTilte, ' + 'because there left no room for it'); + } + }); + var maxWordCount = 2; + var tips = '[TDRadioGroup] radio title please not exceed $maxWordCount words.\n' + '2tabs: 7words maximum\n' + '3tabs: 4words maximum\n' + '4tabs: 2words maximum'; + if (directionalTdRadios.length == 2) { + maxWordCount = 7; + } + if (directionalTdRadios.length == 3) { + maxWordCount = 4; + } + if (directionalTdRadios.length == 4) { + maxWordCount = 2; + } + directionalTdRadios.forEach((radio) { + if ((radio.title?.length ?? 0) > maxWordCount) { + throw FlutterError(tips); + } + }); + } + // 卡片模式要求每个TDRadio必须设置cardMode属性为true,且不能有子标题(空间不够) + if (cardMode == true) { + assert(direction != null && directionalTdRadios != null); + directionalTdRadios!.forEach((element) { + // if use cardMode at TDRadioGroup, then every TDRadio should + // set it's own carMode to true. + if (element.cardMode == false) { + throw FlutterError('if use cardMode at TDRadioGroup, then every ' + 'TDRadio should set it\'s own carMode to true.'); + } + if (element.subTitle != null && direction == Axis.horizontal) { + throw FlutterError('horizontal card style should not have subTilte, ' + 'because there left no room for it'); + } + }); + } + return true; + }()), + super( + child: Container( + clipBehavior: (passThrough ?? false) && direction != Axis.horizontal ? Clip.hardEdge : Clip.none, + decoration: (passThrough ?? false) && direction != Axis.horizontal + ? BoxDecoration(borderRadius: BorderRadius.circular(10)) + : null, + margin: (passThrough ?? false) && direction != Axis.horizontal + ? const EdgeInsets.symmetric(horizontal: 16) + : null, + child: direction == null + ? child! + : (direction == Axis.vertical + ? ListView.separated( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return Container( + margin: cardMode ? const EdgeInsets.symmetric(horizontal: 16) : null, + height: cardMode ? 82 : null, + child: directionalTdRadios[index], + ); + }, + itemCount: directionalTdRadios!.length, + separatorBuilder: (BuildContext context, int index) { + if (cardMode) { + return const SizedBox( + height: 12, + ); + } + return const SizedBox.shrink(); + }, + ) + : Container( + margin: cardMode ? const EdgeInsets.symmetric(horizontal: 16) : null, + height: cardMode ? (directionalTdRadios!.length / rowCount).ceil() * (56 + 10) : null, + alignment: cardMode ? Alignment.topLeft : null, + child: cardMode && rowCount != null + ? GridView.builder( + itemCount: directionalTdRadios!.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisSpacing: 10.0, + mainAxisSpacing: 10.0, + crossAxisCount: rowCount, //一行的 Widget 数量 + mainAxisExtent: 56, + ), + itemBuilder: (BuildContext context, int index) { + return Container( + width: 160.scale, + height: 56, + child: directionalTdRadios![index], + ); + }) + : Column( + children: [ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: directionalTdRadios!.map((e) => Expanded(child: e)).toList(), + ), + if (showDivider) + divider ?? + const TDDivider( + margin: EdgeInsets.only(left: 16), + ) + ], + ), + )), + ), + key: key, + onChangeGroup: (ids) { + onRadioGroupChange?.call(ids.isNotEmpty ? ids[0] : null); + }, + controller: controller, + checkedIds: selectId != null ? [selectId] : null, + maxChecked: 1, + titleMaxLine: titleMaxLine, + contentDirection: contentDirection, + customIconBuilder: customIconBuilder, + customContentBuilder: customContentBuilder, + style: null, + spacing: spacing, + ); + + @override + State createState() { + return TDRadioGroupState(); + } +} + +class TDRadioGroupState extends TDCheckboxGroupState { + @override + bool toggle(String id, bool check, [bool notify = false]) { + checkBoxStates.forEach((key, value) { + checkBoxStates[key] = false; + }); + return super.toggle(id, check, true); + } +} + +typedef OnRadioGroupChange = void Function(String? selectedId); + +// 横向卡片单选框,根据设计师要求'间距保持一致,宽度适应' +// 实现方法为在两个单选框中间增加一个宽度固定的SizedBox,同时每个单选框是Expanded的,这样就能 +// 平分整个Row。 +Iterable horizontalChild(Widget child) sync* { + yield Expanded(child: child); + yield const SizedBox( + width: 12, + ); +} diff --git a/tdesign-component/lib/src/components/rate/td_rate.dart b/tdesign-component/lib/src/components/rate/td_rate.dart new file mode 100644 index 000000000..ffe201382 --- /dev/null +++ b/tdesign-component/lib/src/components/rate/td_rate.dart @@ -0,0 +1,424 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; +import '../../util/iterable_ext.dart'; +import '../../util/throttle.dart'; +import 'td_rate_overlay.dart'; +import 'td_rate_tips.dart'; + +enum PlacementEnum { + none, + top, + bottom, +} + +/// 评分组件 +class TDRate extends StatefulWidget { + const TDRate({ + super.key, + this.allowHalf = false, + this.color, + this.count = 5, + this.disabled = false, + this.gap, + this.icon, + this.placement = PlacementEnum.top, + this.showText = false, + this.size = 24.0, + this.texts = const ['极差', '失望', '一般', '满意', '惊喜'], + this.textWidth = 48.0, + this.builderText, + this.value = 0, + this.onChange, + this.direction = Axis.horizontal, + this.mainAxisAlignment = MainAxisAlignment.start, + this.crossAxisAlignment = CrossAxisAlignment.center, + this.mainAxisSize = MainAxisSize.min, + this.iconTextGap, + }); + + /// 是否允许半选 + final bool? allowHalf; + + /// 评分图标的颜色,示例:[选中颜色] / [选中颜色,未选中颜色],默认:[TDTheme.of(context).warningColor5, TDTheme.of(context).grayColor4] + final List? color; + + /// 评分的数量 + final int? count; + + /// 是否禁用评分 + final bool? disabled; + + /// 评分图标的间距,默认:TDTheme.of(context).spacer8 + final double? gap; + + /// 自定义评分图标,[选中和未选中图标] / [选中图标,未选中图标],默认:[TDIcons.star_filled] + final List? icon; + + /// 选择评分弹框的位置,值为[PlacementEnum.none]表示不显示评分弹框。 + final PlacementEnum? placement; + + /// 是否显示对应的辅助文字 + final bool? showText; + + /// 评分图标的大小 + final double? size; + + /// 评分等级对应的辅助文字, + /// 当[allowHalf]为false时长度应与[count]一致, + /// 当[allowHalf]为true时长度应为[count]的两倍, + /// 自定义值示例:['1分', '2分', '3分', '4分', '5分']。 + final List? texts; + + /// 评分等级对应的辅助文字宽度 + final double? textWidth; + + /// 评分等级对应的辅助文字自定义构建,优先级高于[texts] + /// 配置后,会忽略[texts],[textWidth],[iconTextGap] + final Widget Function(BuildContext context, double value)? builderText; + + /// 选择评分的值 + final double? value; + + /// 评分数改变时触发 + final void Function(double value)? onChange; + + /// 评分图标与辅助文字的布局方向 + final Axis? direction; + + /// 评分图标与辅助文字的主轴对齐方式 + final MainAxisAlignment? mainAxisAlignment; + + /// 评分图标与辅助文字的交叉轴对齐方式 + final CrossAxisAlignment? crossAxisAlignment; + + /// 评分图标与辅助文字主轴方向上如何占用空间 + final MainAxisSize? mainAxisSize; + + /// 评分图标与辅助文字的间距,默认:[TDTheme.of(context).spacer16] + final double? iconTextGap; + + @override + _TDRateState createState() => _TDRateState(); +} + +class _TDRateState extends State with TickerProviderStateMixin { + /// 节流 + final _throttle = Throttle(delay: const Duration(milliseconds: 100)); + + /// 当前选中的评分值 + late double _activeValue; + + /// 根据评分值,获取所在评分的索引 + int _index([double? value]) => ((value ?? _activeValue) - 0.5).floor(); + + /// 每半个评分的GlobalKey + late Map _globalKeys; + + /// 弹框 + late TDRateOverlay _overlay; + + /// 动画 + late List _controller; + late List> _animation; + + /// 隐藏弹框的定时器 + Timer? _hideTipTimer; + + /// 控制显示弹框 + var _showTip = false; + + /// 弹框的尺寸 + var _tipSize = Size.zero; + + /// 每个评分(Row)的Size:<索引, Size> + final _rateSize = {}; + + /// 每个评分(Row)的Offset:<索引, Offset> + final _rateOffset = {}; + + /// 是否点击,否则是滑动 + var _isClick = true; + + @override + void initState() { + super.initState(); + _activeValue = widget.value ?? 0; + _globalKeys = List.generate((widget.count ?? 5) * 2, (index) => index / 2 + 0.5) + .asMap() + .map((index, e) => MapEntry(e, GlobalKey())); + _overlay = TDRateOverlay(context: context, builder: (context) => _buildOverlay())..show(); + _tipSize = Size(widget.allowHalf == true ? 76 : 40, 52); + _controller = List.generate( + widget.count ?? 5, + ((index) => AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ))); + _animation = + List.generate(widget.count ?? 5, ((index) => Tween(begin: 1.0, end: 1.33).animate(_controller[index]))); + } + + @override + void didUpdateWidget(TDRate oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.value != oldWidget.value) { + _activeValue = widget.value ?? 0; + } + if (widget.count != oldWidget.count) { + _globalKeys = List.generate((widget.count ?? 5) * 2, (index) => index / 2 + 0.5) + .asMap() + .map((index, e) => MapEntry(e, GlobalKey())); + } + if (widget.allowHalf != oldWidget.allowHalf) { + _tipSize = Size(widget.allowHalf == true ? 76 : 40, 52); + } + } + + @override + void dispose() { + _overlay.hide(); + _hideTipTimer?.cancel(); + _controller.forEach((controller) => controller.dispose()); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Flex( + direction: widget.direction ?? Axis.horizontal, + mainAxisAlignment: widget.mainAxisAlignment ?? MainAxisAlignment.start, + crossAxisAlignment: widget.crossAxisAlignment ?? CrossAxisAlignment.center, + mainAxisSize: widget.mainAxisSize ?? MainAxisSize.min, + children: [ + GestureDetector( + onTapDown: (event) { + if (widget.disabled == true) { + return; + } + _isClick = true; + }, + onTapUp: (details) { + if (widget.disabled == true) { + return; + } + _changeSelect(details.globalPosition, true); + _hideTip(); + }, + onHorizontalDragUpdate: (details) { + if (widget.disabled == true) { + return; + } + _isClick = false; + _changeSelect(details.globalPosition); + }, + onHorizontalDragEnd: (details) { + if (widget.disabled == true) { + return; + } + _hideTip(); + }, + child: Row( + mainAxisSize: MainAxisSize.min, + children: List.generate(widget.count ?? 5, (index) { + final isLast = index == (widget.count ?? 5) - 1; + return Padding( + padding: EdgeInsets.only(right: isLast ? 0 : widget.gap ?? TDTheme.of(context).spacer8), + child: AnimatedBuilder( + animation: _animation[index], + builder: (context, child) { + return Transform.scale( + scale: _animation[index].value, + child: child, + ); + }, + child: Row( + children: [ + ClipRect( + key: _globalKeys[index + 0.5], + child: Align( + alignment: Alignment.centerLeft, + widthFactor: 0.5, + child: Icon( + _getIcon(value: index + 0.5), + size: widget.size ?? 24, + color: _getIconColor(value: index + 0.5), + ), + ), + ), + ClipRect( + key: _globalKeys[index + 1.0], + child: Align( + alignment: Alignment.centerRight, + widthFactor: 0.5, + child: Icon( + _getIcon(value: index + 1.0), + size: widget.size ?? 24, + color: _getIconColor(value: index + 1.0), + ), + ), + ), + ], + ), + ), + ); + }), + ), + ), + if (widget.showText ?? false) widget.builderText?.call(context, _activeValue) ?? _getDefText() + ], + ); + } + + void _changeSelect(Offset globalPosition, [bool? isTap]) { + _throttle.call(() { + final newIndex = _fingerInsideContainer(globalPosition); + if (newIndex == null) { + return; + } + final diff = newIndex != _activeValue; + if (diff || isTap == true) { + _activeValue = newIndex; + _showTip = newIndex == 0 ? false : true; + _forward(); + _overlay.update(); + if (diff) { + setState(() {}); + widget.onChange?.call(newIndex); + } + } + }); + } + + void _forward() { + _controller.forEach((element) { + element.reverse(); + }); + _controller.getOrNull(_index())?.forward(); + } + + void _reverse() { + _controller.forEach((element) { + element.reverse(); + }); + } + + double? _fingerInsideContainer(Offset globalPosition) { + final rateBox = context.findRenderObject() as RenderBox?; + if (rateBox == null) { + return null; + } + final rateOffset = rateBox.localToGlobal(Offset.zero); + if (globalPosition.dx < rateOffset.dx) { + return 0; + } + for (var entry in _globalKeys.entries) { + final renderBox = entry.value.currentContext?.findRenderObject() as RenderBox?; + if (renderBox != null) { + final localPosition = renderBox.globalToLocal(globalPosition); + final isIn = renderBox.hitTest(BoxHitTestResult(), position: localPosition); + if (isIn) { + var value = widget.allowHalf == true ? entry.key : entry.key.ceil().toDouble(); + var index = _index(value); + if (!_rateSize.containsKey(index) || !_rateOffset.containsKey(index)) { + final parentRenderBox = renderBox.parent as RenderBox; + _rateSize[index] = parentRenderBox.size; + _rateOffset[index] = parentRenderBox.localToGlobal(Offset.zero); + } + return value; + } + } + } + return null; + } + + void _hideTip() { + _hideTipTimer?.cancel(); + _hideTipTimer = Timer( + Duration(milliseconds: _isClick && widget.allowHalf == true ? 3000 : 1000), + () { + _showTip = false; + _isClick = true; + _reverse(); + _overlay.update(); + }, + ); + } + + Widget _getDefText() { + final notRated = _activeValue == 0; + final textIndex = (widget.allowHalf == true ? _activeValue * 2 : _activeValue) - 1; + return Padding( + padding: widget.direction == Axis.horizontal + ? EdgeInsets.only(left: widget.iconTextGap ?? TDTheme.of(context).spacer16) + : EdgeInsets.only(top: widget.iconTextGap ?? TDTheme.of(context).spacer8), + child: SizedBox( + width: widget.textWidth ?? 50, + child: TDText( + notRated ? context.resource.notRated : widget.texts?.getOrNull(textIndex.toInt()) ?? '$_activeValue', + font: notRated ? TDTheme.of(context).fontBodyLarge : TDTheme.of(context).fontTitleMedium, + textColor: notRated ? TDTheme.of(context).fontGyColor4 : TDTheme.of(context).fontGyColor1, + ), + ), + ); + } + + Color _getIconColor({double? value, bool? isActive}) { + return (value != null && _activeValue >= value) || (isActive != null && isActive) + ? widget.color?.getOrNull(0) ?? TDTheme.of(context).warningColor5 + : widget.color?.getOrNull(1) ?? TDTheme.of(context).grayColor4; + } + + IconData _getIcon({double? value, bool? isActive}) { + final selectIcon = widget.icon?.getOrNull(0) ?? TDIcons.star_filled; + final icon = [selectIcon, widget.icon?.getOrNull(1) ?? selectIcon]; + return (value != null && _activeValue >= value) || (isActive != null && isActive) ? icon[0] : icon[1]; + } + + Widget _buildOverlay() { + if (widget.placement == PlacementEnum.none || !_showTip || _activeValue == 0) { + return const SizedBox.shrink(); + } + var index = _index(); + var rateSize = _rateSize[index]; + var rateOffset = _rateOffset[index]; + if (rateSize == null || rateOffset == null) { + return const SizedBox.shrink(); + } + return Positioned( + top: widget.placement == PlacementEnum.top + ? rateOffset.dy - TDTheme.of(context).spacer8 - _tipSize.height + : rateOffset.dy + TDTheme.of(context).spacer8 + rateSize.height, + left: rateOffset.dx - (_tipSize.width - rateSize.width) / 2, + child: TDRateTips( + allowHalf: widget.allowHalf, + activeValue: _activeValue, + icon: _getIcon(isActive: true), + size: widget.size, + getIconColor: _getIconColor, + isClick: _isClick, + sizeCall: (size) { + if (_tipSize.width != size.width || _tipSize.height != size.height) { + _tipSize = size; + _overlay.update(); + } + }, + tipClick: (value) { + _showTip = false; + _isClick = true; + _reverse(); + _overlay.update(); + if (value != _activeValue) { + _activeValue = value; + setState(() {}); + widget.onChange?.call(value); + } + }, + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/rate/td_rate_overlay.dart b/tdesign-component/lib/src/components/rate/td_rate_overlay.dart new file mode 100644 index 000000000..1b7d94668 --- /dev/null +++ b/tdesign-component/lib/src/components/rate/td_rate_overlay.dart @@ -0,0 +1,31 @@ +import 'package:flutter/widgets.dart'; + +class TDRateOverlay { + TDRateOverlay({ + required this.context, + required this.builder, + + }); + final BuildContext context; + final WidgetBuilder builder; + + OverlayEntry? _overlayEntry; + + void show() { + WidgetsBinding.instance.addPostFrameCallback((_) { + _overlayEntry = OverlayEntry( + builder: builder, + ); + + Overlay.of(context).insert(_overlayEntry!); + }); + } + + void update() { + _overlayEntry?.markNeedsBuild(); + } + + void hide() { + _overlayEntry?.remove(); + } +} \ No newline at end of file diff --git a/tdesign-component/lib/src/components/rate/td_rate_tips.dart b/tdesign-component/lib/src/components/rate/td_rate_tips.dart new file mode 100644 index 000000000..0155cf427 --- /dev/null +++ b/tdesign-component/lib/src/components/rate/td_rate_tips.dart @@ -0,0 +1,157 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +/// 评分提示组件 +class TDRateTips extends StatelessWidget { + const TDRateTips({ + Key? key, + this.allowHalf = false, + required this.activeValue, + required this.icon, + this.size, + required this.getIconColor, + required this.sizeCall, + required this.isClick, + required this.tipClick, + }) : super(key: key); + + final bool? allowHalf; + final double activeValue; + final IconData icon; + final double? size; + final Color Function({double? value, bool? isActive}) getIconColor; + final void Function(Size size) sizeCall; + final bool isClick; + final void Function(double value) tipClick; + + int get index => (activeValue - 0.5).floor(); + + @override + Widget build(BuildContext context) { + final _tipKey = GlobalKey(); + WidgetsBinding.instance.addPostFrameCallback((_) { + final renderBox = _tipKey.currentContext?.findRenderObject() as RenderBox?; + sizeCall(renderBox?.size ?? Size.zero); + }); + return Container( + key: _tipKey, + decoration: BoxDecoration( + color: TDTheme.of(context).whiteColor1, + boxShadow: const [ + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.12), + offset: Offset(0, 2), + blurRadius: 4, + spreadRadius: -1, + ), + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.08), + offset: Offset(0, 4), + blurRadius: 5, + spreadRadius: 0, + ), + BoxShadow( + color: Color.fromRGBO(0, 0, 0, 0.05), + offset: Offset(0, 1), + blurRadius: 10, + spreadRadius: 0, + ), + ], + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault), + ), + padding: EdgeInsets.all(TDTheme.of(context).spacer4), + child: Row( + children: [ + GestureDetector( + onTap: () { + if (allowHalf == true && isClick) { + tipClick(index + 0.5); + } + }, + child: Container( + decoration: BoxDecoration( + color: allowHalf == true && index + 0.5 == activeValue && isClick + ? TDTheme.of(context).grayColor3 + : TDTheme.of(context).whiteColor1, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusSmall), + ), + padding: EdgeInsets.only(left: TDTheme.of(context).spacer4, right: TDTheme.of(context).spacer4), + child: Column( + children: [ + Row( + children: [ + ClipRect( + child: Align( + alignment: Alignment.centerLeft, + widthFactor: 0.5, + child: Icon( + icon, + size: size ?? 24, + color: getIconColor(isActive: true), + ), + ), + ), + ClipRect( + child: Align( + alignment: Alignment.centerRight, + widthFactor: 0.5, + child: Icon( + icon, + size: size ?? 24, + color: allowHalf == true + ? (isClick ? getIconColor(isActive: false) : getIconColor(value: index + 1)) + : getIconColor(isActive: true), + ), + ), + ), + ], + ), + Center( + child: TDText( + allowHalf == true ? (isClick ? '${index + 0.5}' : '${activeValue}') : '${index + 1}', + font: TDTheme.of(context).fontBodySmall, + textColor: TDTheme.of(context).fontGyColor1, + ), + ), + ], + ), + ), + ), + if (allowHalf == true && isClick) SizedBox(width: TDTheme.of(context).spacer4), + if (allowHalf == true && isClick) + GestureDetector( + onTap: () { + if (allowHalf == true && isClick) { + tipClick(index + 1.0); + } + }, + child: Container( + decoration: BoxDecoration( + color: index + 1 != activeValue ? TDTheme.of(context).whiteColor1 : TDTheme.of(context).grayColor3, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusSmall), + ), + padding: EdgeInsets.only(left: TDTheme.of(context).spacer4, right: TDTheme.of(context).spacer4), + child: Column( + children: [ + Icon( + icon, + size: size ?? 24, + color: getIconColor(isActive: true), + ), + Center( + child: TDText( + '${index + 1}', + font: TDTheme.of(context).fontBodySmall, + textColor: TDTheme.of(context).fontGyColor1, + ), + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/refresh/td_refresh_header.dart b/tdesign-component/lib/src/components/refresh/td_refresh_header.dart new file mode 100644 index 000000000..236fcdda7 --- /dev/null +++ b/tdesign-component/lib/src/components/refresh/td_refresh_header.dart @@ -0,0 +1,202 @@ +import 'dart:math'; + +import 'package:easy_refresh/easy_refresh.dart'; +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; + +/// TDesign刷新头部 +/// 结合EasyRefresh类实现下拉刷新,继承自Header类,字段含义与父类一致 +class TDRefreshHeader extends Header { + TDRefreshHeader({ + this.key, + this.extent = 48.0, + double? triggerOffset, + this.triggerDistance = 48.0, + bool? clamping, + this.float = false, + Duration? processedDuration, + this.completeDuration, + bool? hapticFeedback, + this.enableHapticFeedback = true, + this.infiniteOffset, + this.enableInfiniteRefresh = false, + bool? infiniteHitOver, + this.overScroll = true, + this.loadingIcon = TDLoadingIcon.circle, + this.backgroundColor, + super.spring, + super.horizontalSpring, + super.readySpringBuilder, + super.horizontalReadySpringBuilder, + super.springRebound, + super.frictionFactor, + super.horizontalFrictionFactor, + super.safeArea = false, + super.hitOver, + super.position, + super.secondaryTriggerOffset, + super.secondaryVelocity, + super.secondaryDimension, + super.secondaryCloseTriggerOffset, + super.notifyWhenInvisible, + super.listenable, + super.triggerWhenReach, + super.triggerWhenRelease, + super.triggerWhenReleaseNoWait, + super.maxOverOffset, + }) : assert((triggerOffset ?? triggerDistance) > 0.0), + assert(extent != null && extent >= 0.0), + assert( + extent != null && ((clamping ?? float) || (triggerOffset ?? triggerDistance) >= extent), + 'The refresh indicator cannot take more space in its final state ' + 'than the amount initially created by overscrolling.'), + super( + triggerOffset: triggerOffset ?? triggerDistance, + clamping: clamping ?? float, + processedDuration: processedDuration ?? completeDuration ?? const Duration(seconds: 1), + hapticFeedback: hapticFeedback ?? enableHapticFeedback, + infiniteOffset: enableInfiniteRefresh ? infiniteOffset : null, + infiniteHitOver: infiniteHitOver ?? overScroll, + ); + + /// Key + final Key? key; + + /// loading样式 + final TDLoadingIcon loadingIcon; + + /// 背景颜色 + final Color? backgroundColor; + + /// Header容器高度 + final double? extent; + + /// 触发刷新任务的偏移量,同[triggerOffset] + final double triggerDistance; + + /// 是否悬浮 + final bool float; + + /// 完成延时 + final Duration? completeDuration; + + /// 开启震动反馈 + final bool enableHapticFeedback; + + /// 是否开启无限刷新 + final bool enableInfiniteRefresh; + + /// 无限刷新偏移量 + @override + final double? infiniteOffset; + + /// 越界滚动([enableInfiniteRefresh]为true或[infiniteOffset]有值时生效) + final bool overScroll; + + @override + Widget build(BuildContext context, IndicatorState state) { + // 不能为水平方向 + assert( + state.axisDirection == AxisDirection.down || state.axisDirection == AxisDirection.up, + 'Widget cannot be horizontal', + ); + return TGIconHeaderWidget( + key: key, + loadingIcon: loadingIcon, + backgroundColor: backgroundColor, + state: state, + refreshIndicatorExtent: extent ?? state.triggerOffset, + ); + } +} + +/// 刷新头部组件 +class TGIconHeaderWidget extends StatefulWidget { + /// loading样式 + final TDLoadingIcon loadingIcon; + + /// 背景颜色 + final Color? backgroundColor; + + /// Indicator properties and state. + final IndicatorState state; + + /// header高度 + final double refreshIndicatorExtent; + + const TGIconHeaderWidget({ + Key? key, + this.backgroundColor, + required this.state, + required this.refreshIndicatorExtent, + required this.loadingIcon, + }) : super(key: key); + + @override + TGIconHeaderWidgetState createState() { + return TGIconHeaderWidgetState(); + } +} + +class TGIconHeaderWidgetState extends State with TickerProviderStateMixin { + IndicatorMode get _refreshState => widget.state.mode; + double get _offset => widget.state.offset; + double get _actualTriggerOffset => widget.state.actualTriggerOffset; + bool get _reverse => widget.state.reverse; + double get _safeOffset => widget.state.safeOffset; + + Widget _buildLoading() => TDLoading( + size: TDLoadingSize.medium, + icon: widget.loadingIcon, + iconColor: TDTheme.of(context).brandNormalColor, + axis: Axis.horizontal, + text: context.resource.refreshing, + textColor: TDTheme.of(context).fontGyColor3, + ); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: _offset, + width: double.infinity, + child: Stack( + children: [ + Positioned( + left: 0, + right: 0, + top: _offset < _actualTriggerOffset + ? -(_actualTriggerOffset - _offset + (_reverse ? _safeOffset : -_safeOffset)) / 2 + : (!_reverse ? _safeOffset : 0), + bottom: _offset < _actualTriggerOffset ? null : (_reverse ? _safeOffset : 0), + height: _offset < _actualTriggerOffset ? _actualTriggerOffset : null, + child: Container( + alignment: Alignment.center, + height: widget.refreshIndicatorExtent, + color: widget.backgroundColor, + child: Visibility( + child: Container( + child: _buildLoading(), + ), + visible: _refreshState == IndicatorMode.processing || _refreshState == IndicatorMode.ready, + replacement: Visibility( + visible: _refreshState != IndicatorMode.inactive, + child: TDText( + _refreshState == IndicatorMode.drag + ? context.resource.pullToRefresh + : _refreshState == IndicatorMode.processed || _refreshState == IndicatorMode.done + ? context.resource.completeRefresh + : context.resource.releaseRefresh, + font: TDTheme.of(context).fontBodyMedium, + textColor: TDTheme.of(context).fontGyColor3, + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/result/td_result.dart b/tdesign-component/lib/src/components/result/td_result.dart new file mode 100644 index 000000000..9b012874d --- /dev/null +++ b/tdesign-component/lib/src/components/result/td_result.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +enum TDResultTheme { defaultTheme, success, warning, error } + +class TDResult extends StatelessWidget { + /// 构造函数,用于初始化Result组件 + const TDResult({ + Key? key, + this.description, + this.icon, + this.titleStyle, + this.theme = TDResultTheme.defaultTheme, // 默认主题样式为defaultTheme + this.title = '', // 默认标题为空字符串 + }) : super(key: key); + +/// 描述文本,用于提供额外信息 + final String? description; +/// 图标组件,用于在结果中显示一个图标 + final Widget? icon; +/// 自定义字体样式,用于设置标题文本的样式 + final TextStyle? titleStyle; +/// 主题样式,定义了结果组件的视觉风格 + final TDResultTheme theme; +/// 标题文本,显示结果的主要信息 + final String title; + + + @override + Widget build(BuildContext context) { + // 根据主题获取默认的图标组件 + Widget displayIcon = icon ?? _getDefaultIconByTheme(context, theme); + // 构建组件布局 + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + child: displayIcon, + ), + // 如果标题不为空,则显示标题 + if (title.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 17), + child: TDText( + title, + textColor: TDTheme.of(context).fontGyColor1, + font: TDTheme.of(context).fontTitleExtraLarge, + style: titleStyle, + )), + // 如果描述不为空,则显示描述 + if (description?.isNotEmpty ?? false) + Padding( + padding: const EdgeInsets.only(top: 8), + child: TDText(description!, + textColor: TDTheme.of(context).fontGyColor2, + font: TDTheme.of(context).fontTitleSmall), + ), + ], + ); + } + + // 根据主题返回对应的默认图标组件 + Widget _getDefaultIconByTheme(BuildContext context, TDResultTheme theme) { + switch (theme) { + case TDResultTheme.success: + return Icon(TDIcons.check_circle, + color: TDTheme.of(context).successNormalColor, size: 70); + case TDResultTheme.warning: + return Icon(TDIcons.error_circle, + color: TDTheme.of(context).warningNormalColor, size: 70); + case TDResultTheme.error: + return Icon(TDIcons.close_circle, + color: TDTheme.of(context).errorNormalColor, size: 70); + default: + return Icon(TDIcons.info_circle, + color: TDTheme.of(context).brandNormalColor, size: 70); + } + } +} diff --git a/tdesign-component/lib/src/components/search/td_search_bar.dart b/tdesign-component/lib/src/components/search/td_search_bar.dart new file mode 100644 index 000000000..f627f0823 --- /dev/null +++ b/tdesign-component/lib/src/components/search/td_search_bar.dart @@ -0,0 +1,332 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; + +/// +/// 搜索框的样式 +/// +enum TDSearchStyle { + square, // 方形 + round, // 圆形 +} + +/// +/// 搜索框对齐方式 +/// +enum TDSearchAlignment { + left, // 默认头部对齐 + center, // 居中 +} + +typedef TDSearchBarEvent = void Function(String value); +typedef TDSearchBarClearEvent = bool? Function(String value); +typedef TDSearchBarCallBack = void Function(); + +class TDSearchBar extends StatefulWidget { + const TDSearchBar({ + Key? key, + this.placeHolder, + this.style = TDSearchStyle.square, + this.alignment = TDSearchAlignment.left, + this.onTextChanged, + this.onSubmitted, + this.onEditComplete, + this.onTapOutside, + this.onInputClick, + this.autoHeight = false, + this.padding = const EdgeInsets.fromLTRB(16, 8, 16, 8), + this.autoFocus = false, + this.mediumStyle = false, + this.cursorHeight, + this.needCancel = false, + this.controller, + this.backgroundColor = Colors.white, + this.action = '', + this.onActionClick, + this.onClearClick, + this.focusNode, + this.inputAction, + this.enabled, + this.readOnly, + }) : super(key: key); + + /// 预设文案 + final String? placeHolder; + + /// 样式 + final TDSearchStyle? style; + + /// 对齐方式,居中或这头部对齐 + final TDSearchAlignment? alignment; + + /// 背景颜色 + final Color? backgroundColor; + + /// 是否自动计算高度 + final bool autoHeight; + + /// 内部填充 + final EdgeInsets padding; + + /// 是否自动获取焦点 + final bool autoFocus; + + /// 是否在导航栏中的样式 + final bool mediumStyle; + + /// 光标的高 + final double? cursorHeight; + + + /// 是否需要取消按钮 + final bool needCancel; + + /// 控制器 + final TextEditingController? controller; + + /// 文字改变回调 + final TDSearchBarEvent? onTextChanged; + + /// 提交回调 + final TDSearchBarEvent? onSubmitted; + + /// 编辑完成回调 + final TDSearchBarCallBack? onEditComplete; + + // 点击输入框外部回调 + final TapRegionCallback? onTapOutside; + + /// 自定义操作文字 + final String action; + + /// 输入框点击事件 + final GestureTapCallback? onInputClick; + /// 自定义操作回调 + final TDSearchBarEvent? onActionClick; + + /// 自定义操作回调 + final TDSearchBarClearEvent? onClearClick; + + /// 自定义焦点 + final FocusNode? focusNode; + + /// 键盘动作类型 + final TextInputAction? inputAction; + + /// 是否禁用 + final bool? enabled; + + /// 是否只读 + final bool? readOnly; + @override + State createState() => _TDSearchBarState(); +} + +class _TDSearchBarState extends State + with TickerProviderStateMixin { + late FocusNode focusNode = FocusNode(); + final TextEditingController controller = TextEditingController(); + final GlobalKey _textFieldKey = GlobalKey(); + + bool clearBtnHide = true; + bool cancelBtnHide = true; + @override + void initState() { + super.initState(); + if(widget.controller==null){ + controller.addListener(() { + var clearVisible = controller.text.isNotEmpty; + _updateClearBtnVisible(clearVisible); + }); + }else{ + widget.controller?.addListener(() { + var clearVisible = widget.controller?.text.isNotEmpty; + _updateClearBtnVisible(clearVisible!); + }); + } + _updateFocusNode(); + } + + void _updateFocusNode() { + focusNode = widget.focusNode ?? focusNode; + focusNode.addListener(() { + setState(() { + cancelBtnHide = !focusNode.hasFocus; + }); + }); + } + + @override + void didUpdateWidget(covariant TDSearchBar oldWidget) { + super.didUpdateWidget(oldWidget); + _updateFocusNode(); + } + + void _updateClearBtnVisible(bool visible) { + setState(() { + clearBtnHide = !visible; + }); + } + + void _cleanInputText(){ + if(!(widget.onClearClick?.call(controller.text) ?? false)){ + // 如果外部没处理,则走默认清除逻辑 + if(widget.controller==null){ + controller.clear(); + }else{ + widget.controller?.clear(); + } + } + } + + Font? getSize(BuildContext context) { + return widget.mediumStyle + ? TDTheme.of(context).fontBodyMedium + : TDTheme.of(context).fontBodyLarge; + } + + Widget actionBtn(BuildContext context, String? text, {String? action, TDSearchBarEvent? onActionClick} ){ + return GestureDetector( + onTap: (){ + onActionClick!(text??''); + }, + child: Container( + padding: const EdgeInsets.only(left: 16), + child: Text(action!, + style: TextStyle( + fontSize: getSize(context)?.size, + color: TDTheme.of(context).brandNormalColor)), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: widget.padding, + height: widget.autoHeight ? double.infinity : 56, + color: widget.backgroundColor, + child: Stack(alignment: AlignmentDirectional.center, children: [ + Row( + children: [ + Expanded( + flex: 1, + child: Container( + height: double.infinity, + decoration: BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.circular( + widget.style == TDSearchStyle.square ? 4 : 28)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox( + width: 12, + ), + Icon( + TDIcons.search, + size: widget.mediumStyle ? 20 : 24, + color: TDTheme.of(context).fontGyColor3, + ), + const Padding(padding: EdgeInsets.only(left: 3)), + Expanded( + flex: 1, + child: Container( + margin: const EdgeInsets.only(bottom: 1),// 为了适配TextField与Text的差异,后续需要做通用适配 + child: TextField( + + key: _textFieldKey, + controller: widget.controller??controller, + autofocus: widget.autoFocus, + cursorColor: TDTheme.of(context).brandNormalColor, + cursorWidth: 1, + cursorHeight:widget.cursorHeight, + textAlign: widget.alignment == TDSearchAlignment.center + ? TextAlign.center + : TextAlign.left, + focusNode: focusNode, + onTap: widget.onInputClick, + onChanged: widget.onTextChanged, + onSubmitted: widget.onSubmitted, + onEditingComplete: widget.onEditComplete, + onTapOutside: widget.onTapOutside, + style: TextStyle( + textBaseline: TextBaseline.ideographic, + fontSize: getSize(context)?.size, + color: TDTheme.of(context).fontGyColor1), + decoration: InputDecoration( + hintText: widget.placeHolder, + hintStyle: TextStyle( + fontSize: getSize(context)?.size, + color: TDTheme.of(context).fontGyColor3, + textBaseline: TextBaseline.ideographic, + overflow: TextOverflow.ellipsis,), + hintMaxLines: 1, + border: InputBorder.none, + isCollapsed: true, + filled: true, + fillColor: TDTheme.of(context).grayColor1, + ), + maxLines: 1, + textInputAction: widget.inputAction, + readOnly:widget.readOnly??false, + enabled:widget.enabled, + cursorOpacityAnimates: false, + ), + ), + ), + const Padding(padding: EdgeInsets.only(right: 9)), + Offstage( + offstage: clearBtnHide, + child: GestureDetector( + onTap: () { + _cleanInputText(); + if (widget.onTextChanged != null) { + widget.onTextChanged!(''); + } + }, + child: Icon( + TDIcons.close_circle_filled, + size: widget.mediumStyle ? 17 : 21, + color: TDTheme.of(context).fontGyColor3, + )), + ), + const Padding(padding: EdgeInsets.only(right: 9)), + ], + ), + ), + ), + widget.action.isNotEmpty + ? actionBtn( + context, + controller.text, + action: widget.action, + onActionClick: widget.onActionClick ?? (String text) {}, + ) + : Offstage( + offstage: cancelBtnHide || !widget.needCancel, + child: GestureDetector( + onTap: () { + _cleanInputText(); + if (widget.onTextChanged != null) { + widget.onTextChanged!(''); + } + focusNode.unfocus(); + }, + child: Container( + padding: const EdgeInsets.only(left: 16), + child: Text(context.resource.cancel, + style: TextStyle( + fontSize: getSize(context)?.size, + color: TDTheme.of(context).brandNormalColor)), + ), + ), + ), + ], + ), + ]), + ); + } + +} diff --git a/tdesign-component/lib/src/components/sidebar/td_sidebar.dart b/tdesign-component/lib/src/components/sidebar/td_sidebar.dart new file mode 100644 index 000000000..ac3128104 --- /dev/null +++ b/tdesign-component/lib/src/components/sidebar/td_sidebar.dart @@ -0,0 +1,301 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import 'td_wrap_sidebar_item.dart'; + +enum TDSideBarStyle { + normal, + outline, +} + +class SideItemProps { + int index; + int value; + bool? disabled; + IconData? icon; + String? label; + TDBadge? badge; + TextStyle? textStyle; + + SideItemProps( + {required this.value, + required this.index, + this.disabled, + this.icon, + this.label, + this.badge, + this.textStyle}); +} + +class TDSideBar extends StatefulWidget { + const TDSideBar({ + Key? key, + this.value, + this.defaultValue, + this.selectedColor, + this.children = const [], + this.onChanged, + this.onSelected, + this.height, + this.controller, + this.contentPadding, + this.selectedTextStyle, + this.style = TDSideBarStyle.normal, + this.loading, + this.loadingWidget, + this.selectedBgColor, + this.unSelectedBgColor, + }) : super(key: key); + + /// 选项值 + final int? value; + + /// 默认值 + final int? defaultValue; + + /// 单项 + final List children; + + /// 选中值发生变化(Controller控制) + final ValueChanged? onChanged; + + /// 选中值发生变化(点击事件) + final ValueChanged? onSelected; + + /// 选中值后颜色 + final Color? selectedColor; + + /// 选中样式 + final TextStyle? selectedTextStyle; + + /// 样式 + final TDSideBarStyle style; + + /// 高度 + final double? height; + + /// 自定义文本框内边距 + final EdgeInsetsGeometry? contentPadding; + + /// 控制器 + final TDSideBarController? controller; + + /// 加载效果 + final bool? loading; + + /// 自定义加载动画 + final Widget? loadingWidget; + + /// 选择的背景颜色 + final Color? selectedBgColor; + + /// 未选择的背景颜色 + final Color? unSelectedBgColor; + + @override + State createState() => _TDSideBarState(); +} + +class _TDSideBarState extends State { + late List displayChildren; + late int? currentValue; + late int? currentIndex; + final _scrollerController = ScrollController(); + final GlobalKey globalKey = GlobalKey(); + final double itemHeight = 56.0; + bool _loading = false; + + // 查找某值对应项 + SideItemProps findSideItem(int value) { + return displayChildren.where((element) => element.value == value).first; + } + + // 选中某值 + void selectValue(int value, {bool needScroll = false}) { + SideItemProps? item; + for (var element in displayChildren) { + if (element.value == value) { + item = element; + } + } + + if (needScroll && item != null) { + try { + var height = globalKey.currentContext!.size!.height; + var offset = _scrollerController.offset; + var distance = item.index * itemHeight - offset; + if (distance + itemHeight > height) { + _scrollerController.animateTo(offset + itemHeight, + duration: const Duration(milliseconds: 100), + curve: Curves.easeIn); + } else if (distance < 0) { + _scrollerController.animateTo(offset - itemHeight, + duration: const Duration(milliseconds: 100), + curve: Curves.easeIn); + } + } catch (e) { + print(e); + } + } + + if (item != null) { + onSelect(item, isController: true); + } + } + + @override + void initState() { + super.initState(); + + _loading = widget.loading ?? widget.controller?.loading ?? false; + // controller注册事件 + if (widget.controller != null) { + widget.controller!.addListener(() { + selectValue(widget.controller!.currentValue, needScroll: true); + _loading = widget.controller!.loading; + getDisplayChildren(); + setState(() {}); + }); + } + + displayChildren = widget.children + .asMap() + .entries + .map((entry) => SideItemProps( + index: entry.key, + disabled: entry.value.disabled, + value: entry.value.value, + icon: entry.value.icon, + label: entry.value.label, + textStyle: entry.value.textStyle, + badge: entry.value.badge)) + .toList(); + + currentValue = widget.value ?? + widget.defaultValue ?? + (displayChildren.isNotEmpty ? displayChildren[0].value : null); + if (currentValue != null) { + try { + final item = findSideItem(currentValue!); + currentIndex = item.index; + } catch (e) { + currentIndex = null; + currentValue = null; + } + } else { + currentIndex = null; + } + } + + void getDisplayChildren() { + if (widget.controller != null && widget.controller!.children.isNotEmpty) { + displayChildren = widget.controller!.children + .asMap() + .entries + .map((entry) => SideItemProps( + index: entry.key, + disabled: entry.value.disabled, + value: entry.value.value, + icon: entry.value.icon, + label: entry.value.label, + textStyle: entry.value.textStyle, + badge: entry.value.badge)) + .toList(); + } else if(widget.children.isNotEmpty) { + displayChildren = widget.children + .asMap() + .entries + .map((entry) => SideItemProps( + index: entry.key, + disabled: entry.value.disabled, + value: entry.value.value, + icon: entry.value.icon, + label: entry.value.label, + textStyle: entry.value.textStyle, + badge: entry.value.badge)) + .toList(); + } else { + displayChildren = []; + } + } + + // 选中某项 + void onSelect(SideItemProps item, {isController = false}) { + if (currentIndex != item.index) { + if (isController) { + if (widget.onChanged != null) { + widget.onChanged!(item.value); + } + } else { + if (widget.onSelected != null) { + widget.onSelected!(item.value); + } + } + + setState(() { + currentIndex = item.index; + }); + } + } + + @override + Widget build(BuildContext context) { + if(_loading) { + widget.controller?.loading = true; + if(widget.loadingWidget != null) { + return widget.loadingWidget!; + } + return SizedBox( + width: MediaQuery.of(context).size.width, + child: const Align( + child: TDLoading(icon: TDLoadingIcon.circle, size: TDLoadingSize.large), + ), + ); + } + return ConstrainedBox( + key: globalKey, + constraints: BoxConstraints( + minWidth: 106, + maxHeight: MediaQuery.of(context).size.height - + MediaQuery.of(context).padding.top), + + child: SizedBox( + height: widget.height ?? MediaQuery.of(context).size.height, + child: MediaQuery.removePadding( + context: context, + removeTop: true, + removeBottom: true, + child: ListView.builder( + physics: const ClampingScrollPhysics(), + itemCount: displayChildren.length, + controller: _scrollerController, + itemBuilder: (BuildContext context, int index) { + var ele = displayChildren[index]; + + return TDWrapSideBarItem( + style: widget.style, + value: ele.value, + icon: ele.icon, + disabled: ele.disabled ?? false, + label: ele.label ?? '', + badge: ele.badge, + textStyle: ele.textStyle, + selected: currentIndex == ele.index, + selectedColor:widget.selectedColor, + selectedTextStyle:widget.selectedTextStyle, + contentPadding:widget.contentPadding, + topAdjacent: currentIndex != null && + currentIndex! + 1 == ele.index, + bottomAdjacent: currentIndex != null && + currentIndex! - 1 == ele.index, + selectedBgColor: widget.selectedBgColor, + unSelectedBgColor: widget.unSelectedBgColor, + onTap: () { + if (!(ele.disabled ?? false)) { + onSelect(ele, isController: false); + } + }, + ); + })))); + } +} diff --git a/tdesign-component/lib/src/components/sidebar/td_sidebar_controller.dart b/tdesign-component/lib/src/components/sidebar/td_sidebar_controller.dart new file mode 100644 index 000000000..d21586f3d --- /dev/null +++ b/tdesign-component/lib/src/components/sidebar/td_sidebar_controller.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +class TDSideBarController extends ChangeNotifier { + int currentValue = 0; + List children = []; + bool _loading = false; + + void selectTo(int value) { + currentValue = value; + notifyListeners(); + } + + void init(List data) { + closeLoading(false, needNotify: false); + children = data; + notifyListeners(); + } + + void closeLoading(bool load, { bool needNotify = true }) { + _loading = load; + if(needNotify) { + notifyListeners(); + } + } + + set loading (bool load) { + _loading = load; + } + + bool get loading => _loading; + + @override + void dispose() { + super.dispose(); + currentValue = 0; + } +} diff --git a/tdesign-component/lib/src/components/sidebar/td_sidebar_item.dart b/tdesign-component/lib/src/components/sidebar/td_sidebar_item.dart new file mode 100644 index 000000000..bb7b20f15 --- /dev/null +++ b/tdesign-component/lib/src/components/sidebar/td_sidebar_item.dart @@ -0,0 +1,35 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +class TDSideBarItem { + const TDSideBarItem({ + Key? key, + this.badge, + this.disabled = false, + this.icon, + this.textStyle, + this.label = '', + this.value = -1, + }); + + /// 徽标 + final TDBadge? badge; + + /// 是否禁用 + final bool disabled; + + /// 图标 + final IconData? icon; + + /// 标签 + final String label; + + /// 标签样式 + final TextStyle? textStyle; + + /// 值 + final int value; +} diff --git a/tdesign-component/lib/src/components/sidebar/td_wrap_sidebar_item.dart b/tdesign-component/lib/src/components/sidebar/td_wrap_sidebar_item.dart new file mode 100644 index 000000000..2277aabe0 --- /dev/null +++ b/tdesign-component/lib/src/components/sidebar/td_wrap_sidebar_item.dart @@ -0,0 +1,202 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +class TDWrapSideBarItem extends StatelessWidget { + const TDWrapSideBarItem({ + Key? key, + this.badge, + required this.disabled, + this.icon, + this.label = '', + this.contentPadding, + this.textStyle = const TextStyle( + fontSize: 16, + height: 1.5, + ), + this.selectedTextStyle, + this.value = -1, + this.selected = false, + this.selectedColor, + this.topAdjacent = false, + this.bottomAdjacent = false, + this.onTap, + this.selectedBgColor, + this.unSelectedBgColor, + required this.style, + }) : super(key: key); + + final TDBadge? badge; + final bool disabled; + final IconData? icon; + final String label; + final EdgeInsetsGeometry? contentPadding; + final TextStyle? textStyle; + final TextStyle? selectedTextStyle; + final int value; + final bool selected; + final Color? selectedColor; + final Color? selectedBgColor; + final Color? unSelectedBgColor; + final bool topAdjacent; + final bool bottomAdjacent; + final VoidCallback? onTap; + final TDSideBarStyle style; + + static const preLineWidth = 3.0; + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: style == TDSideBarStyle.normal ? renderNormalItem(context) : renderOutlineItem(context), + ); + } + + Widget renderNormalItem(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: selectedBgColor ?? Colors.white, + ), + child: Container( + decoration: BoxDecoration( + color: selected + ? (selectedBgColor ?? Colors.white) + : (unSelectedBgColor ?? const Color.fromRGBO(243, 243, 243, 1)), + borderRadius: bottomAdjacent || topAdjacent + ? bottomAdjacent + ? const BorderRadius.only(bottomRight: Radius.circular(9)) + : const BorderRadius.only(topRight: Radius.circular(9)) + : null), + child: Row( + children: [ + renderPreLine(context), + Expanded( + child: Padding( + padding: contentPadding ?? const EdgeInsets.all(16), + child: renderMainContent(context) + )) + ], + ), + ), + ); + } + + Widget renderOutlineItem(BuildContext context) { + return ConstrainedBox( + constraints: const BoxConstraints(minHeight: 56), + child: Container( + // height: 86, + decoration: const BoxDecoration(color: Color.fromRGBO(246, 246, 246, 1)), + padding: const EdgeInsets.all(8), + child: Container( + decoration: BoxDecoration( + color: selected && !disabled ? Colors.white : null, borderRadius: BorderRadius.circular(6)), + padding: const EdgeInsets.all(8), + child: renderMainContent(context), + ), + )); + } + + Widget renderMainContent(BuildContext context) { + + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + renderIcon(context), + Expanded( + child: renderLabel(context)), + if(label.length>4) + renderBadge(context), + // SizedBox( + // width: !disabled && selected ? 0 : preLineWidth, + // ) + ], + ); + } + + Widget renderPreLine(BuildContext context) { + return Visibility( + visible: !disabled && selected, + replacement: const SizedBox( + width: preLineWidth, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: preLineWidth, + height: 14, + decoration: BoxDecoration( + color: selectedTextStyle != null + ? selectedTextStyle?.color + : (selectedColor ?? TDTheme.of(context).brandNormalColor), + borderRadius: BorderRadius.circular(4)), + ) + ], + ), + ); + } + + Widget renderIcon(BuildContext context) { + return Visibility( + visible: icon != null, + child: Padding( + padding: const EdgeInsets.only(right: 2), + child: Icon( + icon, + size: 20, + color: disabled + ? TDTheme.of(context).fontGyColor4 + : selected + ? selectedTextStyle != null + ? selectedTextStyle?.color + : (selectedColor ?? TDTheme.of(context).brandNormalColor) + : Colors.black, + ), + )); + } + + Widget renderLabel(BuildContext context) { + return TDText.rich( + TextSpan( + children: [ + WidgetSpan( + child: TDText( + label, + style: selected ? (selectedTextStyle ?? TextStyle(color: selectedColor)) : textStyle, + fontWeight: selected && !disabled ? FontWeight.w600 : FontWeight.w400, + textColor: disabled + ? TDTheme.of(context).fontGyColor4 + : selected + ? selectedColor ?? TDTheme.of(context).brandNormalColor + : Colors.black, + // forceVerticalCenter: true, + )), + if(label.length<4) + WidgetSpan( + child: SizedBox( + width: 1, + height: 16, + child: Stack( + clipBehavior: Clip.none, + children: [badge != null ? Positioned(top: -6, child: badge!) : Container()], + ), + )) + ], + ), + softWrap: true, + style: selectedTextStyle, + ); + } + + Widget renderBadge(BuildContext context) { + return SizedBox( + width: 1, + height:40, + child: Stack( + clipBehavior: Clip.none, + children: [badge != null ? Positioned(top: -6, child: badge!) : Container()], + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/skeleton/td_skeleton.dart b/tdesign-component/lib/src/components/skeleton/td_skeleton.dart new file mode 100644 index 000000000..8cfa5e94c --- /dev/null +++ b/tdesign-component/lib/src/components/skeleton/td_skeleton.dart @@ -0,0 +1,269 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +/// +/// 骨架图动画 +/// +enum TDSkeletonAnimation { + gradient, // 渐变 + flashed, // 闪烁 +} + +/// +/// 骨架图风格 +/// +enum TDSkeletonTheme { + avatar, // 头像 + image, // 图片 + text, // 文本 + paragraph, // 段落 +} + +class TDSkeleton extends StatefulWidget { + factory TDSkeleton({ + Key? key, + TDSkeletonAnimation? animation, + int delay = 0, + TDSkeletonTheme theme = TDSkeletonTheme.text, + }) { + assert(delay >= 0); + + // 根据风格创建骨架图 + switch (theme) { + case TDSkeletonTheme.avatar: + return TDSkeleton.fromRowCol( + key: key, + animation: animation, + delay: delay, + rowCol: TDSkeletonRowCol( + objects: const [ + [TDSkeletonRowColObj.circle()] + ], + ), + ); + case TDSkeletonTheme.image: + return TDSkeleton.fromRowCol( + key: key, + animation: animation, + delay: delay, + rowCol: TDSkeletonRowCol( + objects: const [ + [ + TDSkeletonRowColObj.rect( + width: 72, + height: 72, + flex: null, + ) + ] + ], + ), + ); + case TDSkeletonTheme.text: + return TDSkeleton.fromRowCol( + key: key, + animation: animation, + delay: delay, + rowCol: TDSkeletonRowCol(objects: const [ + [ + TDSkeletonRowColObj.text(flex: 24), + TDSkeletonRowColObj.spacer(width: 16), + TDSkeletonRowColObj.text(flex: 76), + ], + [TDSkeletonRowColObj.text()], + ]), + ); + case TDSkeletonTheme.paragraph: + return TDSkeleton.fromRowCol( + key: key, + animation: animation, + delay: delay, + rowCol: TDSkeletonRowCol(objects: [ + for (int i = 0; i < 3; i++) [const TDSkeletonRowColObj.text()], + const [ + TDSkeletonRowColObj.text(flex: 55), + TDSkeletonRowColObj.spacer(flex: 45), + ], + ]), + ); + } + } + + /// 从行列框架创建骨架屏 + const TDSkeleton.fromRowCol({ + super.key, + this.animation, + this.delay = 0, + required this.rowCol, + }): assert(delay >= 0); + + /// 动画效果 + final TDSkeletonAnimation? animation; + + /// 延迟显示加载时间 + final int delay; + + /// 自定义行列数量、宽度高度、间距等 + final TDSkeletonRowCol rowCol; + + @override + _TDSkeletonState createState() => _TDSkeletonState(); +} + +class _TDSkeletonState extends State + with SingleTickerProviderStateMixin { + /// 动画控制器 + late final AnimationController? _controller; + + /// 动画效果 + late final Animation? _animation; + + /// 加载状态 + bool _isLoading = true; + + /// 加载控件 + static final _loadingWidget = Container(); + + /// 闪烁透明度 + static const _animationFlashed = .3; + + /// 静态渐变 + static LinearGradient _animationGradient(BuildContext context) => + LinearGradient( + colors: [ + Colors.transparent, + TDTheme.of(context).grayColor4, + Colors.transparent, + ], + // 15 deg + begin: const Alignment(-1, -0.268), + end: const Alignment(1, 0.268), + tileMode: TileMode.clamp, + ); + + @override + void initState() { + super.initState(); + + // 根据动画效果创建动画控制器 + switch (widget.animation) { + case TDSkeletonAnimation.gradient: + _controller = AnimationController( + duration: const Duration(milliseconds: 1500), + vsync: this, + )..repeat(); + _animation = Tween(begin: -1, end: 1).animate(_controller!) + ..addListener(() => setState(() {})); + break; + case TDSkeletonAnimation.flashed: + _controller = AnimationController( + duration: const Duration(seconds: 1), + vsync: this, + )..repeat(reverse: true); + _animation = Tween(begin: 1, end: _animationFlashed).animate(_controller!) + ..addListener(() => setState(() {})); + break; + default: + _controller = null; + _animation = null; + } + + // 延迟显示加载效果 + Future.delayed(Duration(milliseconds: widget.delay), + () => setState(() => _isLoading = false)); + } + + Widget Function(TDSkeletonRowColObj) _buildObj(BuildContext context) => + (TDSkeletonRowColObj obj) { + // 骨架图对象 + Widget skeletonObj = Container( + width: obj.width, + height: obj.height, + margin: obj.margin, + decoration: BoxDecoration( + color: obj.style.background(context), + borderRadius: + BorderRadius.circular(obj.style.borderRadius(context)), + ), + ); + + // 动画效果 + switch (widget.animation) { + case TDSkeletonAnimation.gradient: + skeletonObj = ShaderMask( + blendMode: BlendMode.srcATop, + shaderCallback: (bounds) => _animationGradient(context).createShader( + Rect.fromLTWH( + bounds.width * _animation!.value, + 0, + bounds.width, + bounds.height, + ), + ), + child: skeletonObj, + ); + break; + case TDSkeletonAnimation.flashed: + skeletonObj = Opacity( + opacity: _animation!.value, + child: skeletonObj, + ); + break; + default: + } + + // 根据弹性因子创建弹性布局 + return obj.flex == null + ? skeletonObj + : Flexible(flex: obj.flex!, child: skeletonObj); + }; + + @override + Widget build(BuildContext context) { + // 加载状态返回空容器 + if (_isLoading) { + return _loadingWidget; + } + + if (widget.rowCol.objects.length == 1) { + return widget.rowCol.objects.first.length == 1 + // 单个对象 + ? _buildObj(context)(widget.rowCol.objects.first.first) + // 单行多个对象 + : Flexible( + child: Row( + children: + widget.rowCol.objects.first.map(_buildObj(context)).toList(), + )); + } + + // 多行多个对象 + List skeletonRows = widget.rowCol.objects + .map((row) => Row(children: row.map(_buildObj(context)).toList())) + .toList(); + if (widget.rowCol.style.rowSpacing(context) > 0) { + skeletonRows = skeletonRows + .expand((row) => + [row, SizedBox(height: widget.rowCol.style.rowSpacing(context))]) + .toList() + ..removeLast(); + } // 添加行间距 + var skeletonRowCol = Column(children: skeletonRows); // 行列布局 + + return widget.rowCol.objects + .any((row) => row.any((obj) => obj.flex != null)) + // 添加弹性布局 + ? Flexible( + child: Container( + constraints: BoxConstraints( + maxHeight: widget.rowCol.visualHeight(context)), // 限制最大高度 + child: skeletonRowCol)) + : skeletonRowCol; + } + + @override + void dispose() { + _controller?.dispose(); + super.dispose(); + } +} diff --git a/tdesign-component/lib/src/components/skeleton/td_skeleton_rowcol.dart b/tdesign-component/lib/src/components/skeleton/td_skeleton_rowcol.dart new file mode 100644 index 000000000..30a8d34f8 --- /dev/null +++ b/tdesign-component/lib/src/components/skeleton/td_skeleton_rowcol.dart @@ -0,0 +1,164 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + + +/// 骨架屏样式 +class TDSkeletonRowColStyle { + const TDSkeletonRowColStyle({ + this.rowSpacing = _defaultRowSpacing, + }); + + /// 行间距 + final double Function(BuildContext) rowSpacing; + + /// 默认行间距 + static double _defaultRowSpacing(BuildContext context) => + TDTheme.of(context).spacer16; +} + + +/// 骨架屏行列框架 +class TDSkeletonRowCol { + TDSkeletonRowCol({ + required this.objects, + this.style = const TDSkeletonRowColStyle(), + }) : assert(objects.isNotEmpty && objects.every((row) => row.isNotEmpty)); + + /// 行列对象 + final List> objects; + + /// 样式 + final TDSkeletonRowColStyle style; + + /// 视觉高度 + double visualHeight(BuildContext context) { + var rowSpacing = style.rowSpacing(context); + assert (rowSpacing >= 0); + if (rowSpacing < 0) { + rowSpacing = 0; + } + + return objects + .map((row) => + row.fold(.0, (a, b) => a > b.visualHeight ? a : b.visualHeight)) + .fold(.0, (a, b) => a + b) + + rowSpacing * (objects.length - 1); + } +} + +/// 骨架屏元素样式 +class TDSkeletonRowColObjStyle { + const TDSkeletonRowColObjStyle({ + this.background = _defaultBackground, + this.borderRadius = _textBorderRadius, + }); + + /// 圆形 + const TDSkeletonRowColObjStyle.circle({this.background = _defaultBackground}) + : borderRadius = _circleBorderRadius; + + /// 矩形 + const TDSkeletonRowColObjStyle.rect({this.background = _defaultBackground}) + : borderRadius = _rectBorderRadius; + + /// 文本 + const TDSkeletonRowColObjStyle.text({this.background = _defaultBackground}) + : borderRadius = _textBorderRadius; + + /// 空白占位符 + const TDSkeletonRowColObjStyle.spacer() + : background = _transparentBackground, + borderRadius = _textBorderRadius; + + /// 背景颜色 + final Color Function(BuildContext) background; + + /// 圆角 + final double Function(BuildContext) borderRadius; + + /// 默认背景颜色 + static Color _defaultBackground(BuildContext context) => + TDTheme.of(context).grayColor1; + + /// 透明背景颜色 + static Color _transparentBackground(BuildContext context) => + Colors.transparent; + + /// 圆形圆角 + static double _circleBorderRadius(BuildContext context) => + TDTheme.of(context).radiusCircle; + + /// 矩形圆角 + static double _rectBorderRadius(BuildContext context) => + TDTheme.of(context).radiusDefault; + + /// 文本圆角 + static double _textBorderRadius(BuildContext context) => + TDTheme.of(context).radiusSmall; +} + + +/// 骨架屏元素 +class TDSkeletonRowColObj { + const TDSkeletonRowColObj({ + this.width, + this.height = 16, + this.flex = 1, + this.margin = EdgeInsets.zero, + this.style = const TDSkeletonRowColObjStyle(), + }); + + /// 圆形 + const TDSkeletonRowColObj.circle({ + this.width = 48, + this.height = 48, + this.flex, + this.margin = EdgeInsets.zero, + this.style = const TDSkeletonRowColObjStyle.circle(), + }); + + /// 矩形 + const TDSkeletonRowColObj.rect({ + this.width, + this.height = 16, + this.flex = 1, + this.margin = EdgeInsets.zero, + this.style = const TDSkeletonRowColObjStyle.rect(), + }); + + /// 文本 + const TDSkeletonRowColObj.text({ + this.width, + this.height = 16, + this.flex = 1, + this.margin = EdgeInsets.zero, + this.style = const TDSkeletonRowColObjStyle.text(), + }); + + /// 空白占位符 + const TDSkeletonRowColObj.spacer({ + this.width, + this.height, + this.flex, + this.margin = EdgeInsets.zero, + }) : style = const TDSkeletonRowColObjStyle.spacer(); + + /// 宽度 + final double? width; + + /// 高度 + final double? height; + + /// 弹性因子 + final int? flex; + + /// 间距 + final EdgeInsets margin; + + /// 样式 + final TDSkeletonRowColObjStyle style; + + /// 视觉高度 + double get visualHeight => (height ?? 0) + margin.top + margin.bottom; +} diff --git a/tdesign-component/lib/src/components/slider/td_slider.dart b/tdesign-component/lib/src/components/slider/td_slider.dart new file mode 100644 index 000000000..2ed46943c --- /dev/null +++ b/tdesign-component/lib/src/components/slider/td_slider.dart @@ -0,0 +1,415 @@ +/// +/// Created by arvinwli@tencent.com on 4/24/23. +/// +import 'package:flutter/material.dart'; +import 'td_slider_theme.dart'; + +enum Position { + start, + end, +} + +/// 单滑动选择器 +class TDSlider extends StatefulWidget { + /// 默认值 + final double value; + + /// 自定义盒子样式 + final Decoration? boxDecoration; + + /// 左侧标签 + final String? leftLabel; + + /// 右侧标签 + final String? rightLabel; + + /// 滑动变化监听 + final ValueChanged? onChanged; + + /// 滑动开始监听 + final ValueChanged? onChangeStart; + + /// 滑动结束监听 + final ValueChanged? onChangeEnd; + + /// 样式 + final TDSliderThemeData? sliderThemeData; + + /// Thumb 点击事件 坐标、当前值 + final Function(Offset offset, double value)? onTap; + + /// Thumb 点击浮标文字 坐标、当前值 + final Function(Offset offset, double value)? onThumbTextTap; + + const TDSlider( + {Key? key, + required this.value, + this.boxDecoration, + this.onChanged, + this.sliderThemeData, + this.leftLabel, + this.rightLabel, + this.onChangeStart, + this.onChangeEnd, + this.onTap, + this.onThumbTextTap}) + : super(key: key); + + @override + State createState() { + return TDSliderState(); + } +} + +class TDSliderState extends State { + final GlobalKey _sliderKey = GlobalKey(); + double value = 0; + @override + void initState() { + super.initState(); + value = widget.value; + } + + @override + void didUpdateWidget(covariant TDSlider oldWidget) { + super.didUpdateWidget(oldWidget); + value = widget.value; + } + + bool get enabled => widget.onChanged != null; + + TextStyle get labelTextStyle => TextStyle( + fontSize: 16, + color: enabled ? const Color(0xE6000000) : const Color(0x42000000)); + + Widget get leftLabel => widget.leftLabel?.isNotEmpty == true + ? Padding( + padding: const EdgeInsets.only(left: 16), + child: Text(widget.leftLabel!, style: labelTextStyle), + ) + : Container(); + + Widget get rightLabel => widget.rightLabel?.isNotEmpty == true + ? Padding( + padding: const EdgeInsets.only(right: 16), + child: Text(widget.rightLabel!, style: labelTextStyle), + ) + : Container(); + + @override + Widget build(BuildContext context) { + var tdSliderThemeData = widget.sliderThemeData ?? TDSliderThemeData(); + return Listener( + onPointerDown: (event) { + final sliderBox = + _sliderKey.currentContext?.findRenderObject() as RenderBox?; + if (sliderBox == null || + widget.onThumbTextTap == null || + !tdSliderThemeData.showThumbValue) { + return; + } + + final localOffset = sliderBox.globalToLocal(event.position); + final themeData = widget.sliderThemeData ?? TDSliderThemeData(); + final textRect = themeData.sliderMeasureData.thumbTextRect; + + if (textRect != null && textRect.contains(localOffset)) { + widget.onThumbTextTap?.call(localOffset, value); + } + }, + child: Container( + padding: EdgeInsets.only( + top: (tdSliderThemeData.showScaleValue || + tdSliderThemeData.showThumbValue + ? 16 + : 0) + + 8, + bottom: 8, + ), + decoration: widget.boxDecoration ?? + const BoxDecoration( + color: Colors.white, + ), + child: Row( + children: [ + leftLabel, + const SizedBox(width: 8), + Expanded( + child: Listener( + onPointerDown: (event) { + if (!enabled || widget.onTap == null) { + return; + } + + final sliderBox = _sliderKey.currentContext + ?.findRenderObject() as RenderBox?; + if (sliderBox == null) { + return; + } + + final tapOffset = sliderBox.globalToLocal(event.position); + widget.onTap?.call(tapOffset, value); + }, + child: SliderTheme( + data: tdSliderThemeData.sliderThemeData, + child: Slider( + key: _sliderKey, + value: value, + min: tdSliderThemeData.min, + max: tdSliderThemeData.max, + divisions: tdSliderThemeData.divisions, + onChangeStart: widget.onChangeStart, + onChangeEnd: widget.onChangeEnd, + onChanged: enabled + ? (slideValue) { + setState(() { + value = slideValue; + widget.onChanged?.call(slideValue); + }); + } + : null, + ), + ), + ), + ), + const SizedBox(width: 8), + rightLabel + ], + ), + )); + } +} + +/// 范围滑动选择器 +class TDRangeSlider extends StatefulWidget { + /// 默认值 + final RangeValues value; + + /// 自定义盒子样式 + final Decoration? boxDecoration; + + /// 左侧标签 + final String? leftLabel; + + /// 右侧标签 + + final String? rightLabel; + + /// 滑动变化监听 + final ValueChanged? onChanged; + + /// 滑动开始监听 + + final ValueChanged? onChangeStart; + + /// 滑动结束监听 + final ValueChanged? onChangeEnd; + + /// 样式 + final TDSliderThemeData? sliderThemeData; + + // Thumb 点击事件 位置、坐标、当前值 + final Function(Position position, Offset offset, double value)? onTap; + + /// Thumb 点击浮标文字 位置、坐标、当前值 + final Function(Position position, Offset offset, double value)? + onThumbTextTap; + + const TDRangeSlider( + {Key? key, + required this.value, + this.boxDecoration, + this.onChanged, + this.sliderThemeData, + this.leftLabel, + this.rightLabel, + this.onChangeStart, + this.onChangeEnd, + this.onTap, + this.onThumbTextTap}) + : super(key: key); + + @override + State createState() { + return _TDRangeSliderState(); + } +} + +class _TDRangeSliderState extends State { + RangeValues rangeValues = const RangeValues(0, 100); + final GlobalKey _sliderRangeKey = GlobalKey(); + + @override + void initState() { + super.initState(); + rangeValues = widget.value; + } + + @override + void didUpdateWidget(covariant TDRangeSlider oldWidget) { + super.didUpdateWidget(oldWidget); + rangeValues = widget.value; + } + + bool get enabled => widget.onChanged != null; + + TextStyle get labelTextStyle => TextStyle( + fontSize: 16, + color: enabled ? const Color(0xE6000000) : const Color(0x42000000)); + + Widget get leftLabel => widget.leftLabel?.isNotEmpty == true + ? Padding( + padding: const EdgeInsets.only(left: 16), + child: Text(widget.leftLabel!, style: labelTextStyle), + ) + : Container(); + + Widget get rightLabel => widget.rightLabel?.isNotEmpty == true + ? Padding( + padding: const EdgeInsets.only(right: 16), + child: Text(widget.rightLabel!, style: labelTextStyle), + ) + : Container(); + + @override + Widget build(BuildContext context) { + var tdSliderThemeData = widget.sliderThemeData ?? TDSliderThemeData(); + + return Listener( + onPointerDown: (event) { + final sliderBox = + _sliderRangeKey.currentContext?.findRenderObject() as RenderBox?; + final localOffset = + sliderBox?.globalToLocal(event.position) ?? Offset.zero; + + if (sliderBox == null || + widget.onThumbTextTap == null || + !tdSliderThemeData.showThumbValue) { + return; + } + + final themeData = widget.sliderThemeData ?? TDSliderThemeData(); + final startTextRect = + themeData.sliderMeasureData.startRangeThumbTextRect; + final endTextRect = themeData.sliderMeasureData.endRangeThumbTextRect; + + if (startTextRect?.contains(localOffset) ?? false) { + widget.onThumbTextTap?.call(Position.start, localOffset, rangeValues.start); + } + if (endTextRect?.contains(localOffset) ?? false) { + widget.onThumbTextTap?.call(Position.end, localOffset, rangeValues.end); + } + }, + child: Container( + padding: EdgeInsets.only( + top: (tdSliderThemeData.showScaleValue || + tdSliderThemeData.showThumbValue + ? 16 + : 0) + + 8, + bottom: 8, + ), + decoration: widget.boxDecoration ?? + const BoxDecoration( + color: Colors.white, + ), + child: Row( + children: [ + leftLabel, + const SizedBox(width: 8), + Expanded( + child: Listener( + onPointerDown: (PointerDownEvent event) { + if (!enabled || widget.onTap == null) { + return; + } + + final sliderBox = _sliderRangeKey.currentContext + ?.findRenderObject() as RenderBox?; + if (sliderBox == null) { + return; + } + + final tapOffset = sliderBox.globalToLocal(event.position); + final sliderWidth = sliderBox.size.width; + + final sliderTheme = SliderTheme.of(context); + final thumbShape = sliderTheme.rangeThumbShape; + final thumbSize = thumbShape?.getPreferredSize( + enabled, + widget.sliderThemeData?.divisions != null, + ) ?? + const Size(20, 20); + + final thumbRadius = thumbSize.width / 2; + + // 计算当前值对应的坐标比例 + final min = widget.sliderThemeData?.min ?? 0; + final max = widget.sliderThemeData?.max ?? 100; + final startRatio = (rangeValues.start - min) / (max - min); + final endRatio = (rangeValues.end - min) / (max - min); + + // 计算Thumb中心坐标 + final startCenterX = startRatio * sliderWidth; + final endCenterX = endRatio * sliderWidth; + final verticalCenter = sliderBox.size.height / 2; + + // 检测点击区域 + final isStartTap = + (tapOffset.dx - startCenterX).abs() <= thumbRadius && + (tapOffset.dy - verticalCenter).abs() <= thumbRadius; + final isEndTap = + (tapOffset.dx - endCenterX).abs() <= thumbRadius && + (tapOffset.dy - verticalCenter).abs() <= thumbRadius; + + Position position; + double tappedValue; + + if (isStartTap) { + position = Position.start; + tappedValue = rangeValues.start; + } else if (isEndTap) { + position = Position.end; + tappedValue = rangeValues.end; + } else { + tappedValue = + (tapOffset.dx / sliderWidth) * (max - min) + min; + final startDistance = + (tappedValue - rangeValues.start).abs(); + final endDistance = (tappedValue - rangeValues.end).abs(); + position = startDistance < endDistance + ? Position.start + : Position.end; + } + widget.onTap?.call(position, tapOffset, tappedValue); + }, + child: SliderTheme( + data: tdSliderThemeData.sliderThemeData, + child: RangeSlider( + key: _sliderRangeKey, + values: rangeValues, + min: tdSliderThemeData.min, + max: tdSliderThemeData.max, + divisions: tdSliderThemeData.divisions, + onChanged: widget.onChanged == null + ? null + : (slideValue) { + setState(() { + rangeValues = slideValue; + widget.onChanged?.call(slideValue); + }); + }, + onChangeStart: widget.onChangeStart, + onChangeEnd: widget.onChangeEnd, + ), + ), + ), + ), + const SizedBox(width: 8), + rightLabel, + ], + ), + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/slider/td_slider_theme.dart b/tdesign-component/lib/src/components/slider/td_slider_theme.dart new file mode 100644 index 000000000..8309aacea --- /dev/null +++ b/tdesign-component/lib/src/components/slider/td_slider_theme.dart @@ -0,0 +1,2015 @@ +/// +/// Created by arvinwli@tencent.com on 4/24/23. +/// +import 'dart:math' as math; + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +///刻度显示格式化 +typedef ScaleFormatter = String Function(double value); + +/// 修改系统主题的回调 +typedef OnSliderThemeDataUpdate = SliderThemeData Function( + SliderThemeData sliderThemeData); + +///slider显示样式配置 +class TDSliderThemeData { + ///是否显示游标值 + final bool showThumbValue; + + ///游标上文本样式 + final TextStyle? thumbTextStyle; + + ///disable时游标的样式 + final TextStyle disabledThumbTextStyle; + + ///是否显示刻度值 + final bool showScaleValue; + + ///刻度值的格式化 + final ScaleFormatter? scaleFormatter; + + ///刻度值的样式 + final TextStyle? scaleTextStyle; + + ///disabled状态时刻度的样式 + final TextStyle disabledScaleTextStyle; + + ///分割几块 + final int? divisions; + + ///最小值 + final double min; + + ///最大值 + final double max; + + /// 运行时测量的数据,这里用于组件内部使用 + final SliderMeasureData sliderMeasureData = SliderMeasureData(); + + /// 系统组件库 + SliderThemeData? _sliderThemeData; + + /// 上下文,用于获取主题颜色 + final BuildContext? context; + + /// 是否为胶囊类型 + final bool _capsule; + + /// 激活颜色 + final Color? activeTrackColor; + + /// 非激活颜色 + final Color? inactiveTrackColor; + + /// 普通构建方法 + TDSliderThemeData({ + this.context, + this.showScaleValue = false, + this.showThumbValue = false, + this.divisions, + TextStyle? scaleTextStyle, + TextStyle? disabledScaleTextStyle, + TextStyle? thumbTextStyle, + TextStyle? disabledThumbTextStyle, + this.min = 0.0, + this.max = 1.0, + this.scaleFormatter, + this.activeTrackColor, + this.inactiveTrackColor, + SliderThemeData? sliderThemeData, + }) : scaleTextStyle = scaleTextStyle ?? + TextStyle(fontSize: 14, color: TDTheme.of(context).fontGyColor1), + disabledScaleTextStyle = disabledScaleTextStyle ?? + TextStyle(fontSize: 14, color: TDTheme.of(context).fontGyColor4), + thumbTextStyle = thumbTextStyle ?? + TextStyle(fontSize: 14, color: TDTheme.of(context).fontGyColor1), + disabledThumbTextStyle = disabledThumbTextStyle ?? + TextStyle(fontSize: 14, color: TDTheme.of(context).fontGyColor4), + _sliderThemeData = sliderThemeData, + _capsule = false; + + /// 胶囊型构建方法 + TDSliderThemeData.capsule({ + this.context, + this.showScaleValue = false, + this.showThumbValue = false, + this.divisions, + TextStyle? scaleTextStyle, + TextStyle? disabledScaleTextStyle, + TextStyle? thumbTextStyle, + TextStyle? disabledThumbTextStyle, + this.min = 0.0, + this.max = 1.0, + this.scaleFormatter, + this.activeTrackColor, + this.inactiveTrackColor, + SliderThemeData? sliderThemeData, + }) : scaleTextStyle = scaleTextStyle ?? + TextStyle(fontSize: 14, color: TDTheme.of(context).fontGyColor1), + disabledScaleTextStyle = disabledScaleTextStyle ?? + TextStyle(fontSize: 14, color: TDTheme.of(context).fontGyColor4), + thumbTextStyle = thumbTextStyle ?? + TextStyle(fontSize: 14, color: TDTheme.of(context).fontGyColor1), + disabledThumbTextStyle = disabledThumbTextStyle ?? + TextStyle(fontSize: 14, color: TDTheme.of(context).fontGyColor4), + _sliderThemeData = sliderThemeData, + _capsule = true; + + /// 获取系统主题 + SliderThemeData get sliderThemeData { + _sliderThemeData ??= _capsule ? capsule() : normal(); + return _sliderThemeData!; + } + + /// 更新系统主题 + void updateSliderThemeData(OnSliderThemeDataUpdate onSliderThemeDataUpdate) { + _sliderThemeData = onSliderThemeDataUpdate(sliderThemeData); + } + + /// 构建普通系统主题 + SliderThemeData normal() { + return SliderThemeData( + trackHeight: 4, + activeTrackColor: + activeTrackColor ?? TDTheme.of(context).brandNormalColor, + inactiveTrackColor: inactiveTrackColor ?? TDTheme.of(context).grayColor4, + disabledActiveTrackColor: TDTheme.of(context).brandDisabledColor, + disabledInactiveTrackColor: TDTheme.of(context).grayColor2, + activeTickMarkColor: TDTheme.of(context).brandNormalColor, + inactiveTickMarkColor: TDTheme.of(context).grayColor4, + disabledActiveTickMarkColor: TDTheme.of(context).brandDisabledColor, + disabledInactiveTickMarkColor: TDTheme.of(context).grayColor2, + thumbColor: Colors.white, + disabledThumbColor: Colors.white, + overlayShape: const TDNoOverlayShape(), + tickMarkShape: TDRoundSliderTickMarkShape(themeData: this), + thumbShape: TDRoundSliderThumbShape(themeData: this), + trackShape: TDRoundedRectSliderTrackShape(themeData: this), + rangeTickMarkShape: TDRoundRangeSliderTickMarkShape(themeData: this), + rangeThumbShape: TDRoundRangeSliderThumbShape(themeData: this), + rangeTrackShape: TDRoundedRectRangeSliderTrackShape(themeData: this), + showValueIndicator: ShowValueIndicator.never, + ); + } + + /// 构建胶囊型系统主题 + SliderThemeData capsule() { + return SliderThemeData( + trackShape: TDCapsuleRectSliderTrackShape(themeData: this), + tickMarkShape: TDCapsuleSliderTickMarkShape(themeData: this), + thumbShape: TDCapsuleSliderThumbShape(themeData: this), + rangeTrackShape: TDCapsuleRectRangeSliderTrackShape(themeData: this), + rangeTickMarkShape: TDCapsuleRangeSliderTickMarkShape(themeData: this), + rangeThumbShape: TDCapsuleRangeSliderThumbShape(themeData: this), + activeTickMarkColor: TDTheme.of(context).grayColor3, + inactiveTickMarkColor: TDTheme.of(context).grayColor3, + disabledActiveTickMarkColor: TDTheme.of(context).grayColor3, + disabledInactiveTickMarkColor: TDTheme.of(context).grayColor3, + thumbColor: Colors.white, + disabledThumbColor: Colors.white, + trackHeight: 24, + activeTrackColor: + activeTrackColor ?? TDTheme.of(context).brandNormalColor, + inactiveTrackColor: inactiveTrackColor ?? TDTheme.of(context).grayColor4, + disabledActiveTrackColor: TDTheme.of(context).brandDisabledColor, + disabledInactiveTrackColor: TDTheme.of(context).grayColor2, + overlayShape: const TDNoOverlayShape(), + showValueIndicator: ShowValueIndicator.never, + ); + } + + /// 复制数据,该方法配[updateSliderThemeData]可以快速服用对象属性 + TDSliderThemeData copyWith({ + SliderThemeData? themeData, + bool? showScaleValue, + bool? showThumbValue, + TextStyle? disabledScaleTextStyle, + TextStyle? disabledThumbTextStyle, + TextStyle? scaleTextStyle, + TextStyle? thumbTextStyle, + int? divisions, + double? min, + double? max, + ScaleFormatter? scaleFormatter, + Color? activeTrackColor, + Color? inactiveTrackColor, + }) { + return TDSliderThemeData( + showScaleValue: showScaleValue ?? this.showScaleValue, + showThumbValue: showThumbValue ?? this.showThumbValue, + disabledScaleTextStyle: + disabledScaleTextStyle ?? this.disabledScaleTextStyle, + disabledThumbTextStyle: + disabledThumbTextStyle ?? this.disabledThumbTextStyle, + scaleTextStyle: scaleTextStyle ?? this.scaleTextStyle, + thumbTextStyle: thumbTextStyle ?? this.thumbTextStyle, + divisions: divisions ?? this.divisions, + min: min ?? this.min, + max: max ?? this.max, + scaleFormatter: scaleFormatter ?? this.scaleFormatter, + activeTrackColor: activeTrackColor ?? this.activeTrackColor, + inactiveTrackColor: inactiveTrackColor ?? this.inactiveTrackColor, + ); + } +} + +/// 内部测量数据 +class SliderMeasureData { + Rect? trackerRect; + Offset? thumbCenter; + Rect? thumbTextRect; + Rect? startRangeThumbTextRect; + Rect? endRangeThumbTextRect; +} + +/// +///Slider轨道绘制 +/// +class TDRoundedRectSliderTrackShape extends SliderTrackShape + with BaseSliderTrackShape { + /// Create a slider track that draws two rectangles with rounded outer edges. + const TDRoundedRectSliderTrackShape({required this.themeData}); + + final TDSliderThemeData themeData; + + @override + void paint( + PaintingContext context, + Offset offset, { + required RenderBox parentBox, + required SliderThemeData sliderTheme, + required Animation enableAnimation, + required TextDirection textDirection, + required Offset thumbCenter, + Offset? secondaryOffset, + bool isDiscrete = false, + bool isEnabled = false, + double additionalActiveTrackHeight = 2, + }) { + assert(sliderTheme.disabledActiveTrackColor != null); + assert(sliderTheme.disabledInactiveTrackColor != null); + assert(sliderTheme.activeTrackColor != null); + assert(sliderTheme.inactiveTrackColor != null); + assert(sliderTheme.thumbShape != null); + // If the slider [SliderThemeData.trackHeight] is less than or equal to 0, + // then it makes no difference whether the track is painted or not, + // therefore the painting can be a no-op. + if (sliderTheme.trackHeight == null || sliderTheme.trackHeight! <= 0) { + return; + } + + // Assign the track segment paints, which are leading: active and + // trailing: inactive. + final activeTrackColorTween = ColorTween( + begin: sliderTheme.disabledActiveTrackColor, + end: sliderTheme.activeTrackColor); + final inactiveTrackColorTween = ColorTween( + begin: sliderTheme.disabledInactiveTrackColor, + end: sliderTheme.inactiveTrackColor); + final activePaint = Paint() + ..color = activeTrackColorTween.evaluate(enableAnimation)!; + final inactivePaint = Paint() + ..color = inactiveTrackColorTween.evaluate(enableAnimation)!; + final Paint leftTrackPaint; + final Paint rightTrackPaint; + switch (textDirection) { + case TextDirection.ltr: + leftTrackPaint = activePaint; + rightTrackPaint = inactivePaint; + break; + case TextDirection.rtl: + leftTrackPaint = inactivePaint; + rightTrackPaint = activePaint; + break; + } + + final trackRect = getPreferredRect( + parentBox: parentBox, + offset: offset, + sliderTheme: sliderTheme, + isEnabled: isEnabled, + isDiscrete: isDiscrete, + ); + //record size ,Use it when calculating thumb text position + themeData.sliderMeasureData.trackerRect = trackRect; + final trackRadius = Radius.circular(trackRect.height / 2); + final activeTrackRadius = + Radius.circular((trackRect.height + additionalActiveTrackHeight) / 2); + + context.canvas.drawRRect( + RRect.fromLTRBAndCorners( + trackRect.left, + (textDirection == TextDirection.rtl) + ? trackRect.top - (additionalActiveTrackHeight / 2) + : trackRect.top, + thumbCenter.dx, + (textDirection == TextDirection.rtl) + ? trackRect.bottom + (additionalActiveTrackHeight / 2) + : trackRect.bottom, + topLeft: (textDirection == TextDirection.ltr) + ? activeTrackRadius + : trackRadius, + bottomLeft: (textDirection == TextDirection.ltr) + ? activeTrackRadius + : trackRadius, + ), + leftTrackPaint, + ); + context.canvas.drawRRect( + RRect.fromLTRBAndCorners( + thumbCenter.dx, + (textDirection == TextDirection.rtl) + ? trackRect.top - (additionalActiveTrackHeight / 2) + : trackRect.top, + trackRect.right, + (textDirection == TextDirection.rtl) + ? trackRect.bottom + (additionalActiveTrackHeight / 2) + : trackRect.bottom, + topRight: (textDirection == TextDirection.rtl) + ? activeTrackRadius + : trackRadius, + bottomRight: (textDirection == TextDirection.rtl) + ? activeTrackRadius + : trackRadius, + ), + rightTrackPaint, + ); + } +} + +/// +///游标的绘制 +/// +class TDRoundSliderThumbShape extends SliderComponentShape { + /// Create a slider thumb that draws a circle. + const TDRoundSliderThumbShape({ + this.enabledThumbRadius = 10.0, + this.disabledThumbRadius, + this.elevation = 4.0, + this.pressedElevation = 4.0, + required this.themeData, + }); + + /// The preferred radius of the round thumb shape when the slider is enabled. + /// + /// If it is not provided, then the Material Design default of 10 is used. + final double enabledThumbRadius; + + /// The preferred radius of the round thumb shape when the slider is disabled. + /// + /// If no disabledRadius is provided, then it is equal to the + /// [enabledThumbRadius] + final double? disabledThumbRadius; + + double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius; + + /// The resting elevation adds shadow to the unpressed thumb. + /// + /// The default is 1. + /// + /// Use 0 for no shadow. The higher the value, the larger the shadow. For + /// example, a value of 12 will create a very large shadow. + /// + final double elevation; + + /// The pressed elevation adds shadow to the pressed thumb. + /// + /// The default is 6. + /// + /// Use 0 for no shadow. The higher the value, the larger the shadow. For + /// example, a value of 12 will create a very large shadow. + final double pressedElevation; + + final TDSliderThemeData themeData; + + @override + Size getPreferredSize(bool isEnabled, bool isDiscrete) { + return Size.fromRadius( + isEnabled == true ? enabledThumbRadius : _disabledThumbRadius); + } + + @override + void paint( + PaintingContext context, + Offset center, { + required Animation activationAnimation, + required Animation enableAnimation, + required bool isDiscrete, + required TextPainter labelPainter, + required RenderBox parentBox, + required SliderThemeData sliderTheme, + required TextDirection textDirection, + required double value, + required double textScaleFactor, + required Size sizeWithOverflow, + }) { + assert(sliderTheme.disabledThumbColor != null); + assert(sliderTheme.thumbColor != null); + + final canvas = context.canvas; + final radiusTween = Tween( + begin: _disabledThumbRadius, + end: enabledThumbRadius, + ); + final colorTween = ColorTween( + begin: sliderTheme.disabledThumbColor, + end: sliderTheme.thumbColor, + ); + + final color = colorTween.evaluate(enableAnimation)!; + final radius = radiusTween.evaluate(enableAnimation); + + final elevationTween = Tween( + begin: elevation, + end: pressedElevation, + ); + + final evaluatedElevation = elevationTween.evaluate(activationAnimation); + final path = Path() + ..addArc( + Rect.fromCenter( + center: center, width: 2 * radius, height: 2 * radius), + 0, + math.pi * 2); + + var paintShadows = true; + + if (paintShadows) { + canvas.drawShadow( + path, const Color.fromRGBO(0, 0, 0, 0.5), evaluatedElevation, true); + } + // draw thumb text + if (themeData.showThumbValue && + themeData.sliderMeasureData.trackerRect != null) { + var trackerRect = themeData.sliderMeasureData.trackerRect!; + var ratio = (center.dx - trackerRect.left) / + (trackerRect.right - trackerRect.left); + //计算滑块的值 + var value = (themeData.max - themeData.min) * ratio + themeData.min; + //格式化显示 + var formatterValue = themeData.scaleFormatter == null + ? value.toStringAsFixed(2) + : themeData.scaleFormatter!(value); + //绘制数值 + var painter = TextPainter( + text: TextSpan( + text: '$formatterValue', + style: enableAnimation.value > 0 + ? themeData.thumbTextStyle + : themeData.disabledThumbTextStyle), + textDirection: TextDirection.ltr, + textAlign: TextAlign.center) + ..layout(maxWidth: 100); + + var textPosition = Offset( + center.dx - painter.width / 2, center.dy - painter.height - 14); + painter.paint(context.canvas, textPosition); + themeData.sliderMeasureData.thumbTextRect = Rect.fromLTWH( + center.dx - painter.width / 2, + center.dy - painter.height - 14, + painter.width, + painter.height); + } + var paint = Paint(); + paint.color = color; + canvas.drawCircle( + center, + radius, + paint, + ); + canvas.drawArc( + Rect.fromCircle(center: center, radius: radius), + 0, + 2 * math.pi, + false, + Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = TDTheme.of().grayColor3); + } +} + +///系统用于绘制Overlay,这里不做绘制,只做slider的宽高计算 +class TDNoOverlayShape extends SliderComponentShape { + const TDNoOverlayShape(); + + @override + Size getPreferredSize(bool isEnabled, bool isDiscrete) { + return const Size(0, 40); + } + + @override + void paint( + PaintingContext context, + Offset center, { + required Animation activationAnimation, + required Animation enableAnimation, + required bool isDiscrete, + required TextPainter labelPainter, + required RenderBox parentBox, + required SliderThemeData sliderTheme, + required TextDirection textDirection, + required double value, + required double textScaleFactor, + required Size sizeWithOverflow, + }) {} +} + +/// +/// 刻度绘制 +/// +class TDRoundSliderTickMarkShape extends SliderTickMarkShape { + /// Create a slider tick mark that draws a circle. + const TDRoundSliderTickMarkShape({ + this.tickMarkRadius, + required this.themeData, + }); + + /// The preferred radius of the round tick mark. + /// + /// If it is not provided, then 1/4 of the [SliderThemeData.trackHeight] is used. + final double? tickMarkRadius; + + final TDSliderThemeData themeData; + + @override + Size getPreferredSize({ + required SliderThemeData sliderTheme, + required bool isEnabled, + }) { + assert(sliderTheme.trackHeight != null); + // The tick marks are tiny circles. If no radius is provided, then the + // radius is defaulted to be a fraction of the + // [SliderThemeData.trackHeight]. The fraction is 1/4. + return Size.fromRadius(tickMarkRadius ?? 4); + } + + @override + void paint( + PaintingContext context, + Offset center, { + required RenderBox parentBox, + required SliderThemeData sliderTheme, + required Animation enableAnimation, + required TextDirection textDirection, + required Offset thumbCenter, + required bool isEnabled, + }) { + assert(sliderTheme.disabledActiveTickMarkColor != null); + assert(sliderTheme.disabledInactiveTickMarkColor != null); + assert(sliderTheme.activeTickMarkColor != null); + assert(sliderTheme.inactiveTickMarkColor != null); + // The paint color of the tick mark depends on its position relative + // to the thumb and the text direction. + Color? begin; + Color? end; + switch (textDirection) { + case TextDirection.ltr: + final isTickMarkRightOfThumb = center.dx > thumbCenter.dx; + begin = isTickMarkRightOfThumb + ? sliderTheme.disabledInactiveTickMarkColor + : sliderTheme.disabledActiveTickMarkColor; + end = isTickMarkRightOfThumb + ? sliderTheme.inactiveTickMarkColor + : sliderTheme.activeTickMarkColor; + break; + case TextDirection.rtl: + final isTickMarkLeftOfThumb = center.dx < thumbCenter.dx; + begin = isTickMarkLeftOfThumb + ? sliderTheme.disabledInactiveTickMarkColor + : sliderTheme.disabledActiveTickMarkColor; + end = isTickMarkLeftOfThumb + ? sliderTheme.inactiveTickMarkColor + : sliderTheme.activeTickMarkColor; + break; + } + final paint = Paint() + ..color = ColorTween(begin: begin, end: end).evaluate(enableAnimation)!; + + // The tick marks are tiny circles that are the same height as the track. + final tickMarkRadius = getPreferredSize( + isEnabled: isEnabled, + sliderTheme: sliderTheme, + ).width / + 2; + if (tickMarkRadius > 0 && themeData.showScaleValue) { + assert(themeData.divisions != null); + var rect = sliderTheme.trackShape + ?.getPreferredRect(parentBox: parentBox, sliderTheme: sliderTheme); + if (rect != null && themeData.divisions! > 0) { + //轨道的高度 + var trackHeight = rect.bottom - rect.top; + //最左边的刻度中心到最右边刻度中心的长度 + var markWidth = (rect.right - rect.left) - trackHeight; + //最左边刻度的起点 + var markStart = rect.left + trackHeight / 2; + //每个刻度的宽度 + var perWidth = markWidth / themeData.divisions!; + assert(perWidth > 0); + //计算当前是第几个刻度 + var index = ((center.dx - markStart) / perWidth).round(); + //获取当前刻度的值 + var value = themeData.min + + index * ((themeData.max - themeData.min) / themeData.divisions!); + //格式化数值 + var valueFormatter = themeData.scaleFormatter != null + ? themeData.scaleFormatter!(value) + : value.toString(); + //绘制刻度的值 + var painter = TextPainter( + text: TextSpan( + text: valueFormatter, + style: enableAnimation.value > 0 + ? themeData.scaleTextStyle + : themeData.disabledScaleTextStyle), + textDirection: TextDirection.ltr, + textAlign: TextAlign.center) + ..layout(maxWidth: 100); + //绘制的x + var x = center.dx - painter.size.width / 2; + if (index == 0) { + x = center.dx - trackHeight; + } else if (index == themeData.divisions) { + x = center.dx - painter.size.width + trackHeight; + } + painter.paint( + context.canvas, Offset(x, center.dy - painter.height - 14)); + } + //绘制刻度 + context.canvas.drawCircle(center, tickMarkRadius, paint); + } + } +} + +/// Base track shape that provides an implementation of [getPreferredRect] for +/// default sizing. +/// +/// The height is set from [SliderThemeData.trackHeight] and the width of the +/// parent box less the larger of the widths of [SliderThemeData.rangeThumbShape] and +/// [SliderThemeData.overlayShape]. +/// +/// See also: +/// +/// * [RectangularRangeSliderTrackShape], which is a track shape with sharp +/// rectangular edges +mixin TDBaseRangeSliderTrackShape { + /// Returns a rect that represents the track bounds that fits within the + /// [Slider]. + /// + /// The width is the width of the [Slider] or [RangeSlider], but padded by + /// the max of the overlay and thumb radius. The height is defined by the + /// [SliderThemeData.trackHeight]. + /// + /// The [Rect] is centered both horizontally and vertically within the slider + /// bounds. + Rect getPreferredRect({ + required RenderBox parentBox, + Offset offset = Offset.zero, + required SliderThemeData sliderTheme, + bool isEnabled = false, + bool isDiscrete = false, + }) { + assert(sliderTheme.rangeThumbShape != null); + assert(sliderTheme.overlayShape != null); + final thumbWidth = sliderTheme.rangeThumbShape! + .getPreferredSize(isEnabled, isDiscrete) + .width; + final overlayWidth = + sliderTheme.overlayShape!.getPreferredSize(isEnabled, isDiscrete).width; + final trackHeight = sliderTheme.trackHeight!; + assert(overlayWidth >= 0); + assert(trackHeight >= 0); + + final trackLeft = offset.dx + math.max(overlayWidth / 2, thumbWidth / 2); + final trackTop = offset.dy + (parentBox.size.height - trackHeight) / 2; + final trackRight = + trackLeft + parentBox.size.width - math.max(thumbWidth, overlayWidth); + final trackBottom = trackTop + trackHeight; + final rect = Rect.fromLTRB(math.min(trackLeft, trackRight), trackTop, + math.max(trackLeft, trackRight), trackBottom); + // If the parentBox'size less than slider's size the trackRight will be less than trackLeft, so switch them. + return rect; + } +} + +/// The default shape of a [TDRangeSlider]'s track. +/// +/// It paints a solid colored rectangle with rounded edges, vertically centered +/// in the `parentBox`. The track rectangle extends to the bounds of the +/// `parentBox`, but is padded by the larger of [RoundSliderOverlayShape]'s +/// radius and [RoundRangeSliderThumbShape]'s radius. The height is defined by +/// the [SliderThemeData.trackHeight]. The color is determined by the +/// [RangeSlider]'s enabled state and the track segment's active state which are +/// defined by: +/// [SliderThemeData.activeTrackColor], +/// [SliderThemeData.inactiveTrackColor], +/// [SliderThemeData.disabledActiveTrackColor], +/// [SliderThemeData.disabledInactiveTrackColor]. +/// +/// {@macro flutter.material.RangeSliderTickMarkShape.paint.trackSegment} +/// +/// ![A range slider widget, consisting of 5 divisions and showing the rounded rect range slider track shape.] +/// (https://flutter.github.io/assets-for-api-docs/assets/material/rounded_rect_range_slider_track_shape.png) +/// +/// See also: +/// +/// * [RangeSlider], for the component that is meant to display this shape. +/// * [SliderThemeData], where an instance of this class is set to inform the +/// slider of the visual details of the its track. +/// * [RangeSliderTrackShape], which can be used to create custom shapes for +/// the [RangeSlider]'s track. +/// * [RectangularRangeSliderTrackShape], for a similar track with sharp edges. +class TDRoundedRectRangeSliderTrackShape extends RangeSliderTrackShape + with TDBaseRangeSliderTrackShape { + /// Create a slider track with rounded outer edges. + /// + /// The middle track segment is the selected range and is active, and the two + /// outer track segments are inactive. + const TDRoundedRectRangeSliderTrackShape({required this.themeData}); + + final TDSliderThemeData themeData; + + @override + void paint( + PaintingContext context, + Offset offset, { + required RenderBox parentBox, + required SliderThemeData sliderTheme, + required Animation enableAnimation, + required Offset startThumbCenter, + required Offset endThumbCenter, + bool isEnabled = false, + bool isDiscrete = false, + required TextDirection textDirection, + double additionalActiveTrackHeight = 0, + }) { + assert(sliderTheme.disabledActiveTrackColor != null); + assert(sliderTheme.disabledInactiveTrackColor != null); + assert(sliderTheme.activeTrackColor != null); + assert(sliderTheme.inactiveTrackColor != null); + assert(sliderTheme.rangeThumbShape != null); + + if (sliderTheme.trackHeight == null || sliderTheme.trackHeight! <= 0) { + return; + } + + // Assign the track segment paints, which are left: active, right: inactive, + // but reversed for right to left text. + final activeTrackColorTween = ColorTween( + begin: sliderTheme.disabledActiveTrackColor, + end: sliderTheme.activeTrackColor, + ); + final inactiveTrackColorTween = ColorTween( + begin: sliderTheme.disabledInactiveTrackColor, + end: sliderTheme.inactiveTrackColor, + ); + final activePaint = Paint() + ..color = activeTrackColorTween.evaluate(enableAnimation)!; + final inactivePaint = Paint() + ..color = inactiveTrackColorTween.evaluate(enableAnimation)!; + + final Offset leftThumbOffset; + final Offset rightThumbOffset; + switch (textDirection) { + case TextDirection.ltr: + leftThumbOffset = startThumbCenter; + rightThumbOffset = endThumbCenter; + break; + case TextDirection.rtl: + leftThumbOffset = endThumbCenter; + rightThumbOffset = startThumbCenter; + break; + } + final thumbSize = + sliderTheme.rangeThumbShape!.getPreferredSize(isEnabled, isDiscrete); + final thumbRadius = thumbSize.width / 2; + assert(thumbRadius > 0); + + final trackRect = getPreferredRect( + parentBox: parentBox, + offset: offset, + sliderTheme: sliderTheme, + isEnabled: isEnabled, + isDiscrete: isDiscrete, + ); + themeData.sliderMeasureData.trackerRect = trackRect; + + final trackRadius = Radius.circular(trackRect.height / 2); + + context.canvas.drawRRect( + RRect.fromLTRBAndCorners( + trackRect.left, + trackRect.top, + leftThumbOffset.dx, + trackRect.bottom, + topLeft: trackRadius, + bottomLeft: trackRadius, + ), + inactivePaint, + ); + context.canvas.drawRect( + Rect.fromLTRB( + leftThumbOffset.dx, + trackRect.top - (additionalActiveTrackHeight / 2), + rightThumbOffset.dx, + trackRect.bottom + (additionalActiveTrackHeight / 2), + ), + activePaint, + ); + context.canvas.drawRRect( + RRect.fromLTRBAndCorners( + rightThumbOffset.dx, + trackRect.top, + trackRect.right, + trackRect.bottom, + topRight: trackRadius, + bottomRight: trackRadius, + ), + inactivePaint, + ); + } +} + +/// The default shape of a [RangeSlider]'s thumbs. +/// +/// There is a shadow for the resting and pressed state. +/// +/// ![A slider widget, consisting of 5 divisions and showing the round range slider thumb shape.] +/// (https://flutter.github.io/assets-for-api-docs/assets/material/round_range_slider_thumb_shape.png) +/// +/// See also: +/// +/// * [RangeSlider], which includes thumbs defined by this shape. +/// * [SliderTheme], which can be used to configure the thumb shapes of all +/// range sliders in a widget subtree. +class TDRoundRangeSliderThumbShape extends RangeSliderThumbShape { + /// Create a slider thumb that draws a circle. + const TDRoundRangeSliderThumbShape({ + this.enabledThumbRadius = 10.0, + this.disabledThumbRadius, + this.elevation = 3.0, + this.pressedElevation = 3.0, + required this.themeData, + }); + + /// The preferred radius of the round thumb shape when the slider is enabled. + /// + /// If it is not provided, then the Material Design default of 10 is used. + final double enabledThumbRadius; + + /// The preferred radius of the round thumb shape when the slider is disabled. + /// + /// If no disabledRadius is provided, then it is equal to the + /// [enabledThumbRadius]. + final double? disabledThumbRadius; + + double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius; + + /// The resting elevation adds shadow to the unpressed thumb. + /// + /// The default is 1. + final double elevation; + + /// The pressed elevation adds shadow to the pressed thumb. + /// + /// The default is 6. + final double pressedElevation; + + final TDSliderThemeData themeData; + + @override + Size getPreferredSize(bool isEnabled, bool isDiscrete) { + return Size.fromRadius( + isEnabled == true ? enabledThumbRadius : _disabledThumbRadius); + } + + @override + void paint( + PaintingContext context, + Offset center, { + required Animation activationAnimation, + required Animation enableAnimation, + bool isDiscrete = false, + bool isEnabled = false, + bool? isOnTop, + required SliderThemeData sliderTheme, + TextDirection? textDirection, + Thumb? thumb, + bool? isPressed, + }) { + assert(sliderTheme.showValueIndicator != null); + assert(sliderTheme.overlappingShapeStrokeColor != null); + final canvas = context.canvas; + final radiusTween = Tween( + begin: _disabledThumbRadius, + end: enabledThumbRadius, + ); + final colorTween = ColorTween( + begin: sliderTheme.disabledThumbColor, + end: sliderTheme.thumbColor, + ); + final radius = radiusTween.evaluate(enableAnimation); + final elevationTween = Tween( + begin: elevation, + end: pressedElevation, + ); + + // Add a stroke of 1dp around the circle if this thumb would overlap + // the other thumb. + if (isOnTop ?? false) { + final strokePaint = Paint() + ..color = sliderTheme.overlappingShapeStrokeColor! + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke; + canvas.drawCircle(center, radius, strokePaint); + } + + final color = colorTween.evaluate(enableAnimation)!; + + final evaluatedElevation = + isPressed! ? elevationTween.evaluate(activationAnimation) : elevation; + final shadowPath = Path() + ..addArc( + Rect.fromCenter( + center: center, width: 2 * radius, height: 2 * radius), + 0, + math.pi * 2); + + var paintShadows = true; + if (paintShadows) { + canvas.drawShadow(shadowPath, const Color.fromRGBO(0, 0, 0, 0.5), + evaluatedElevation, true); + } + if (themeData.showThumbValue && + themeData.sliderMeasureData.trackerRect != null) { + var trackerRect = themeData.sliderMeasureData.trackerRect!; + var ratio = (center.dx - trackerRect.left) / + (trackerRect.right - trackerRect.left); + //计算滑块的值 + var value = (themeData.max - themeData.min) * ratio + themeData.min; + //格式化显示 + var formatterValue = themeData.scaleFormatter == null + ? value.toStringAsFixed(2) + : themeData.scaleFormatter!(value); + //绘制数值 + var painter = TextPainter( + text: TextSpan( + text: '$formatterValue', + style: enableAnimation.value > 0 + ? themeData.thumbTextStyle + : themeData.disabledThumbTextStyle), + textDirection: TextDirection.ltr, + textAlign: TextAlign.center) + ..layout(maxWidth: 100); + var textPosition = Offset( + center.dx - painter.width / 2, center.dy - painter.height - 14); + painter.paint(context.canvas, textPosition); + if (thumb == Thumb.start) { + themeData.sliderMeasureData.startRangeThumbTextRect = Rect.fromLTWH( + center.dx - painter.width / 2, + center.dy - painter.height - 14, + painter.width, + painter.height); + } else { + themeData.sliderMeasureData.endRangeThumbTextRect = Rect.fromLTWH( + center.dx - painter.width / 2, + center.dy - painter.height - 14, + painter.width, + painter.height); + } + } + //绘制游标 + canvas.drawCircle( + center, + radius, + Paint()..color = color, + ); + } +} + +/// The default shape of each [RangeSlider] tick mark. +/// +/// Tick marks are only displayed if the slider is discrete, which can be done +/// by setting the [RangeSlider.divisions] to an integer value. +/// +/// It paints a solid circle, centered on the track. +/// The color is determined by the [Slider]'s enabled state and track's active +/// states. These colors are defined in: +/// [SliderThemeData.activeTrackColor], +/// [SliderThemeData.inactiveTrackColor], +/// [SliderThemeData.disabledActiveTrackColor], +/// [SliderThemeData.disabledInactiveTrackColor]. +/// +/// ![A slider widget, consisting of 5 divisions and showing the round range slider tick mark shape.] +/// (https://flutter.github.io/assets-for-api-docs/assets/material/round_range_slider_tick_mark_shape.png ) +/// +/// See also: +/// +/// * [RangeSlider], which includes tick marks defined by this shape. +/// * [SliderTheme], which can be used to configure the tick mark shape of all +/// sliders in a widget subtree. +class TDRoundRangeSliderTickMarkShape extends RangeSliderTickMarkShape { + /// Create a range slider tick mark that draws a circle. + const TDRoundRangeSliderTickMarkShape({ + this.tickMarkRadius, + required this.themeData, + }); + + /// The preferred radius of the round tick mark. + /// + /// If it is not provided, then 1/4 of the [SliderThemeData.trackHeight] is used. + final double? tickMarkRadius; + + final TDSliderThemeData themeData; + + @override + Size getPreferredSize({ + required SliderThemeData sliderTheme, + bool isEnabled = false, + }) { + return Size.fromRadius(tickMarkRadius ?? 4); + } + + @override + void paint( + PaintingContext context, + Offset center, { + required RenderBox parentBox, + required SliderThemeData sliderTheme, + required Animation enableAnimation, + required Offset startThumbCenter, + required Offset endThumbCenter, + bool isEnabled = false, + required TextDirection textDirection, + }) { + assert(sliderTheme.disabledActiveTickMarkColor != null); + assert(sliderTheme.disabledInactiveTickMarkColor != null); + assert(sliderTheme.activeTickMarkColor != null); + assert(sliderTheme.inactiveTickMarkColor != null); + + final bool isBetweenThumbs; + switch (textDirection) { + case TextDirection.ltr: + isBetweenThumbs = + startThumbCenter.dx < center.dx && center.dx < endThumbCenter.dx; + break; + case TextDirection.rtl: + isBetweenThumbs = + endThumbCenter.dx < center.dx && center.dx < startThumbCenter.dx; + break; + } + final begin = isBetweenThumbs + ? sliderTheme.disabledActiveTickMarkColor + : sliderTheme.disabledInactiveTickMarkColor; + final end = isBetweenThumbs + ? sliderTheme.activeTickMarkColor + : sliderTheme.inactiveTickMarkColor; + final paint = Paint() + ..color = ColorTween(begin: begin, end: end).evaluate(enableAnimation)!; + + // The tick marks are tiny circles that are the same height as the track. + final tickMarkRadius = getPreferredSize( + isEnabled: isEnabled, + sliderTheme: sliderTheme, + ).width / + 2; + if (tickMarkRadius > 0 && themeData.showScaleValue) { + assert(themeData.divisions != null); + var rect = sliderTheme.rangeTrackShape + ?.getPreferredRect(parentBox: parentBox, sliderTheme: sliderTheme); + if (rect != null && themeData.divisions! > 0) { + //轨道的高度 + var trackHeight = rect.bottom - rect.top; + //最左边的刻度中心到最右边刻度中心的长度 + var markWidth = (rect.right - rect.left) - trackHeight; + //最左边刻度的起点 + var markStart = rect.left + trackHeight / 2; + //每个刻度的宽度 + var perWidth = markWidth / themeData.divisions!; + assert(perWidth > 0); + //计算当前是第几个刻度 + var index = ((center.dx - markStart) / perWidth).round(); + //获取当前刻度的值 + var value = themeData.min + + index * ((themeData.max - themeData.min) / themeData.divisions!); + //格式化数值 + var valueFormatter = themeData.scaleFormatter != null + ? themeData.scaleFormatter!(value) + : value.toString(); + //绘制刻度的值 + var painter = TextPainter( + text: TextSpan( + text: valueFormatter, + style: enableAnimation.value > 0 + ? themeData.scaleTextStyle + : themeData.disabledScaleTextStyle), + textDirection: TextDirection.ltr, + textAlign: TextAlign.center) + ..layout(maxWidth: 100); + //绘制的x + var x = center.dx - painter.size.width / 2; + if (index == 0) { + x = center.dx - trackHeight; + } else if (index == themeData.divisions) { + x = center.dx - painter.size.width + trackHeight; + } + painter.paint( + context.canvas, Offset(x, center.dy - painter.height - 14)); + } + context.canvas.drawCircle(center, tickMarkRadius, paint); + } + } +} + +mixin TDCapsuleTrackShape { + Offset adjustThumbCenter(Offset thumbCenter, Rect trackRect) { + final value = (thumbCenter.dx - trackRect.left) / trackRect.width; + final x = value * (trackRect.width - 24) + trackRect.left + 12; + return Offset(x, thumbCenter.dy); + } +} + +abstract interface class TDCapsuleRectThemeData { + final TDSliderThemeData themeData; + TDCapsuleRectThemeData({required this.themeData}); +} + +mixin TDCapsuleRectAdjustment implements TDCapsuleRectThemeData { + // 是否有分区 + hasDivisions() { + return themeData.divisions != null && themeData.divisions! > 0; + } + + // 胶囊类型适配样式边距 trackHeight / 2,默认是 12 + extraPadding({trackHeight = 24}) { + if (hasDivisions()) { + return 0; + } + return trackHeight / 2; + } + + // 胶囊类型数值计算修正边距 trackHeight / 2,默认是 12 + trackPadding({trackHeight = 24}) { + if (hasDivisions()) { + return trackHeight / 2; + } + return 0; + } +} + +/// +///Slider轨道绘制 +/// +class TDCapsuleRectSliderTrackShape extends SliderTrackShape with BaseSliderTrackShape, TDCapsuleTrackShape, TDCapsuleRectAdjustment { + + /// Create a slider track that draws two rectangles with rounded outer edges. + const TDCapsuleRectSliderTrackShape( + {this.trackColorWhenShowScale = const Color(0xFFE7E7E7), + required this.themeData}); + + final Color trackColorWhenShowScale; + + @override + final TDSliderThemeData themeData; + + @override + Rect getPreferredRect( + {required RenderBox parentBox, + Offset offset = Offset.zero, + required SliderThemeData sliderTheme, + bool isEnabled = false, + bool isDiscrete = false}) { + var rect = super.getPreferredRect( + parentBox: parentBox, + offset: offset, + sliderTheme: sliderTheme, + isEnabled: isEnabled, + isDiscrete: isDiscrete, + ); + final padding = extraPadding(); + var realRect = Rect.fromLTRB(rect.left + padding, rect.top, rect.right - padding, rect.bottom); + themeData.sliderMeasureData.trackerRect = realRect; + return realRect; + } + + @override + void paint( + PaintingContext context, + Offset offset, { + required RenderBox parentBox, + required SliderThemeData sliderTheme, + required Animation enableAnimation, + required TextDirection textDirection, + required Offset thumbCenter, + Offset? secondaryOffset, + bool isDiscrete = false, + bool isEnabled = false, + double additionalActiveTrackHeight = 3, + }) { + assert(sliderTheme.disabledActiveTrackColor != null); + assert(sliderTheme.disabledInactiveTrackColor != null); + assert(sliderTheme.activeTrackColor != null); + assert(sliderTheme.inactiveTrackColor != null); + assert(sliderTheme.thumbShape != null); + // If the slider [SliderThemeData.trackHeight] is less than or equal to 0, + // then it makes no difference whether the track is painted or not, + // therefore the painting can be a no-op. + if (sliderTheme.trackHeight == null || sliderTheme.trackHeight! <= 0) { + return; + } + // Assign the track segment paints, which are leading: active and + // trailing: inactive. + var showScale = themeData.showScaleValue; + final activeTrackColorTween = ColorTween( + begin: sliderTheme.disabledActiveTrackColor, + end: sliderTheme.activeTrackColor); + final inactiveTrackColorTween = ColorTween( + begin: sliderTheme.disabledInactiveTrackColor, + end: showScale + ? trackColorWhenShowScale + : sliderTheme.inactiveTrackColor); + final activePaint = Paint() + ..color = activeTrackColorTween.evaluate(enableAnimation)!; + final inactivePaint = Paint() + ..color = inactiveTrackColorTween.evaluate(enableAnimation)!; + final Paint activeTrackPaint; + final Paint inactiveTrackPaint; + switch (textDirection) { + case TextDirection.ltr: + activeTrackPaint = activePaint; + inactiveTrackPaint = inactivePaint; + break; + case TextDirection.rtl: + activeTrackPaint = inactivePaint; + inactiveTrackPaint = activePaint; + break; + } + + final trackRect = getPreferredRect( + parentBox: parentBox, + offset: offset, + sliderTheme: sliderTheme, + isEnabled: isEnabled, + isDiscrete: isDiscrete, + ); + themeData.sliderMeasureData.trackerRect = trackRect; + final trackRadius = Radius.circular(trackRect.height / 2); + final activeTrackRadius = + Radius.circular((trackRect.height - additionalActiveTrackHeight) / 2); + + final padding = extraPadding(); + context.canvas.drawRRect( + RRect.fromLTRBAndCorners(trackRect.left - padding, trackRect.top, trackRect.right + padding, trackRect.bottom, + topLeft: trackRadius, bottomLeft: trackRadius, topRight: trackRadius, bottomRight: trackRadius), + inactiveTrackPaint, + ); + context.canvas.drawRRect( + RRect.fromLTRBAndCorners( + trackRect.left - padding + additionalActiveTrackHeight, + trackRect.top + additionalActiveTrackHeight, + thumbCenter.dx, + trackRect.bottom - additionalActiveTrackHeight, + topLeft: activeTrackRadius, + bottomLeft: activeTrackRadius, + ), + activeTrackPaint, + ); + if (themeData.showScaleValue) { + final inactiveSecondPaint = Paint() + ..color = sliderTheme.inactiveTrackColor!; + context.canvas.drawRRect( + RRect.fromLTRBAndCorners( + thumbCenter.dx, + trackRect.top + additionalActiveTrackHeight, + trackRect.right + padding - additionalActiveTrackHeight, + trackRect.bottom - additionalActiveTrackHeight, + topRight: activeTrackRadius, + bottomRight: activeTrackRadius, + ), + inactiveSecondPaint, + ); + } + } +} + +/// +///游标的绘制 +/// +class TDCapsuleSliderThumbShape extends SliderComponentShape with TDCapsuleRectAdjustment { + /// Create a slider thumb that draws a circle. + const TDCapsuleSliderThumbShape({ + this.enabledThumbRadius = 10.0, + this.disabledThumbRadius, + this.elevation = 4.0, + this.pressedElevation = 4.0, + required this.themeData, + }); + + /// The preferred radius of the round thumb shape when the slider is enabled. + /// + /// If it is not provided, then the Material Design default of 10 is used. + final double enabledThumbRadius; + + /// The preferred radius of the round thumb shape when the slider is disabled. + /// + /// If no disabledRadius is provided, then it is equal to the + /// [enabledThumbRadius] + final double? disabledThumbRadius; + + double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius; + + /// The resting elevation adds shadow to the unpressed thumb. + /// + /// The default is 1. + /// + /// Use 0 for no shadow. The higher the value, the larger the shadow. For + /// example, a value of 12 will create a very large shadow. + /// + final double elevation; + + /// The pressed elevation adds shadow to the pressed thumb. + /// + /// The default is 6. + /// + /// Use 0 for no shadow. The higher the value, the larger the shadow. For + /// example, a value of 12 will create a very large shadow. + final double pressedElevation; + + @override + final TDSliderThemeData themeData; + + @override + Size getPreferredSize(bool isEnabled, bool isDiscrete) { + return Size.fromRadius( + isEnabled == true ? enabledThumbRadius : _disabledThumbRadius); + } + + @override + void paint( + PaintingContext context, + Offset center, { + required Animation activationAnimation, + required Animation enableAnimation, + required bool isDiscrete, + required TextPainter labelPainter, + required RenderBox parentBox, + required SliderThemeData sliderTheme, + required TextDirection textDirection, + required double value, + required double textScaleFactor, + required Size sizeWithOverflow, + }) { + assert(sliderTheme.disabledThumbColor != null); + assert(sliderTheme.thumbColor != null); + + final canvas = context.canvas; + final radiusTween = Tween( + begin: _disabledThumbRadius, + end: enabledThumbRadius, + ); + final colorTween = ColorTween( + begin: sliderTheme.disabledThumbColor, + end: sliderTheme.thumbColor, + ); + + final color = colorTween.evaluate(enableAnimation)!; + final radius = radiusTween.evaluate(enableAnimation); + + final elevationTween = Tween( + begin: elevation, + end: pressedElevation, + ); + final evaluatedElevation = elevationTween.evaluate(activationAnimation); + final path = Path() + ..addArc( + Rect.fromCenter( + center: center, width: 2 * radius, height: 2 * radius), + 0, + math.pi * 2); + + var paintShadows = true; + + if (paintShadows) { + canvas.drawShadow( + path, const Color.fromRGBO(0, 0, 0, 0.5), evaluatedElevation, true); + } + // draw thumb text + if (themeData.showThumbValue && + themeData.sliderMeasureData.trackerRect != null) { + var trackerRect = themeData.sliderMeasureData.trackerRect!; + final padding = trackPadding(); + var ratio = (center.dx - trackerRect.left - padding) / (trackerRect.right - trackerRect.left - padding * 2); + //计算滑块的值 + var value = (themeData.max - themeData.min) * ratio + themeData.min; + //格式化显示 + var formatterValue = themeData.scaleFormatter == null + ? value.toStringAsFixed(2) + : themeData.scaleFormatter!(value); + //绘制数值 + var painter = TextPainter( + text: TextSpan( + text: '$formatterValue', + style: enableAnimation.value > 0 + ? themeData.thumbTextStyle + : themeData.disabledThumbTextStyle), + textDirection: TextDirection.ltr, + textAlign: TextAlign.center) + ..layout(maxWidth: 100); + painter.paint( + context.canvas, + Offset(center.dx - painter.size.width / 2, + center.dy - painter.height - 14)); + } + var paint = Paint(); + paint.color = color; + canvas.drawCircle( + center, + radius, + paint, + ); + canvas.drawArc( + Rect.fromCircle(center: center, radius: radius), + 0, + 2 * math.pi, + false, + Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = TDTheme.of().grayColor1); + } +} + +/// +/// 刻度绘制 +/// +class TDCapsuleSliderTickMarkShape extends SliderTickMarkShape { + /// Create a slider tick mark that draws a circle. + const TDCapsuleSliderTickMarkShape({ + this.tickMarkRadius, + required this.themeData, + }); + + /// The preferred radius of the round tick mark. + /// + /// If it is not provided, then 1/4 of the [SliderThemeData.trackHeight] is used. + final double? tickMarkRadius; + + final TDSliderThemeData themeData; + + @override + Size getPreferredSize({ + required SliderThemeData sliderTheme, + required bool isEnabled, + }) { + assert(sliderTheme.trackHeight != null); + // The tick marks are tiny circles. If no radius is provided, then the + // radius is defaulted to be a fraction of the + // [SliderThemeData.trackHeight]. The fraction is 1/4. + return Size.fromRadius(tickMarkRadius ?? 4); + } + + @override + void paint( + PaintingContext context, + Offset center, { + required RenderBox parentBox, + required SliderThemeData sliderTheme, + required Animation enableAnimation, + required TextDirection textDirection, + required Offset thumbCenter, + required bool isEnabled, + }) { + assert(sliderTheme.disabledActiveTickMarkColor != null); + assert(sliderTheme.disabledInactiveTickMarkColor != null); + assert(sliderTheme.activeTickMarkColor != null); + assert(sliderTheme.inactiveTickMarkColor != null); + // The paint color of the tick mark depends on its position relative + // to the thumb and the text direction. + // Color? begin; + // Color? end; + // // final paint = Paint()..color = ColorTween(begin: begin, end: end).evaluate(enableAnimation)!; + // final paint = Paint() + // ..strokeWidth = 2 + // ..color = ColorTween(begin: begin, end: end).evaluate(enableAnimation)!; + // The tick marks are tiny circles that are the same height as the track. + final tickMarkRadius = getPreferredSize( + isEnabled: isEnabled, + sliderTheme: sliderTheme, + ).width / + 2; + var dx = center.dx; + if (tickMarkRadius > 0 && themeData.showScaleValue) { + assert(themeData.divisions != null); + var rect = sliderTheme.trackShape + ?.getPreferredRect(parentBox: parentBox, sliderTheme: sliderTheme); + if (rect != null && themeData.divisions! > 0) { + //轨道的高度 + var trackHeight = rect.bottom - rect.top; + //最左边的刻度中心到最右边刻度中心的长度 + var markWidth = (rect.right - rect.left) - trackHeight; + //最左边刻度的起点 + var markStart = rect.left + trackHeight / 2; + //每个刻度的宽度 + var perWidth = markWidth / themeData.divisions!; + assert(perWidth > 0); + //计算当前是第几个刻度 + var index = ((center.dx - markStart) / perWidth).round(); + //获取当前刻度的值 + var value = themeData.min + + index * ((themeData.max - themeData.min) / themeData.divisions!); + //修正x坐标 + dx = rect.left + + index * (((rect.right - rect.left) / themeData.divisions!)); + //格式化数值 + var valueFormatter = themeData.scaleFormatter != null + ? themeData.scaleFormatter!(value) + : value.toString(); + //绘制刻度的值 + var painter = TextPainter( + text: TextSpan( + text: valueFormatter, + style: enableAnimation.value > 0 + ? themeData.scaleTextStyle + : themeData.disabledScaleTextStyle), + textDirection: TextDirection.ltr, + textAlign: TextAlign.center) + ..layout(maxWidth: 100); + var x = dx - painter.size.width / 2; + painter.paint(context.canvas, Offset(x, center.dy - painter.height - 16)); + + // 第一个和最后一个不展示 + if (index > 0 && index < themeData.divisions!) { + final isBetweenThumbs = thumbCenter.dx > center.dx; + final begin = + isBetweenThumbs ? sliderTheme.disabledActiveTickMarkColor : sliderTheme.disabledInactiveTickMarkColor; + final end = isBetweenThumbs ? sliderTheme.activeTickMarkColor : sliderTheme.inactiveTickMarkColor; + final paint = Paint() + ..strokeWidth = 2 + ..color = ColorTween(begin: begin, end: end).evaluate(enableAnimation)!; + context.canvas.drawLine(Offset(dx, themeData.sliderMeasureData.trackerRect!.top + 3), + Offset(dx, themeData.sliderMeasureData.trackerRect!.bottom - 3), paint); + } + } + } + } +} + +/// The default shape of a [TDRangeSlider]'s track. +/// +/// It paints a solid colored rectangle with rounded edges, vertically centered +/// in the `parentBox`. The track rectangle extends to the bounds of the +/// `parentBox`, but is padded by the larger of [RoundSliderOverlayShape]'s +/// radius and [RoundRangeSliderThumbShape]'s radius. The height is defined by +/// the [SliderThemeData.trackHeight]. The color is determined by the +/// [RangeSlider]'s enabled state and the track segment's active state which are +/// defined by: +/// [SliderThemeData.activeTrackColor], +/// [SliderThemeData.inactiveTrackColor], +/// [SliderThemeData.disabledActiveTrackColor], +/// [SliderThemeData.disabledInactiveTrackColor]. +/// +/// {@macro flutter.material.RangeSliderTickMarkShape.paint.trackSegment} +/// +/// ![A range slider widget, consisting of 5 divisions and showing the rounded rect range slider track shape.] +/// (https://flutter.github.io/assets-for-api-docs/assets/material/rounded_rect_range_slider_track_shape.png) +/// +/// See also: +/// +/// * [RangeSlider], for the component that is meant to display this shape. +/// * [SliderThemeData], where an instance of this class is set to inform the +/// slider of the visual details of the its track. +/// * [RangeSliderTrackShape], which can be used to create custom shapes for +/// the [RangeSlider]'s track. +/// * [RectangularRangeSliderTrackShape], for a similar track with sharp edges. +class TDCapsuleRectRangeSliderTrackShape extends RangeSliderTrackShape with TDBaseRangeSliderTrackShape, TDCapsuleRectAdjustment { + final Color trackColorWhenShowScale; + + /// Create a slider track with rounded outer edges. + /// + /// The middle track segment is the selected range and is active, and the two + /// outer track segments are inactive. + const TDCapsuleRectRangeSliderTrackShape( + {this.trackColorWhenShowScale = const Color(0xFFE7E7E7), + required this.themeData}); + + @override + final TDSliderThemeData themeData; + + @override + Rect getPreferredRect( + {required RenderBox parentBox, + Offset offset = Offset.zero, + required SliderThemeData sliderTheme, + bool isEnabled = false, + bool isDiscrete = false}) { + var rect = super.getPreferredRect( + parentBox: parentBox, + offset: offset, + sliderTheme: sliderTheme, + isEnabled: isEnabled, + isDiscrete: isDiscrete, + ); + final padding = extraPadding(); + return Rect.fromLTRB(rect.left + padding, rect.top, rect.right - padding, rect.bottom); + } + + @override + void paint( + PaintingContext context, + Offset offset, { + required RenderBox parentBox, + required SliderThemeData sliderTheme, + required Animation enableAnimation, + required Offset startThumbCenter, + required Offset endThumbCenter, + bool isEnabled = false, + bool isDiscrete = false, + required TextDirection textDirection, + double additionalActiveTrackHeight = 3, + }) { + assert(sliderTheme.disabledActiveTrackColor != null); + assert(sliderTheme.disabledInactiveTrackColor != null); + assert(sliderTheme.activeTrackColor != null); + assert(sliderTheme.inactiveTrackColor != null); + assert(sliderTheme.rangeThumbShape != null); + + if (sliderTheme.trackHeight == null || sliderTheme.trackHeight! <= 0) { + return; + } + var showScale = themeData.showScaleValue; + // Assign the track segment paints, which are left: active, right: inactive, + // but reversed for right to left text. + final activeTrackColorTween = ColorTween( + begin: sliderTheme.disabledActiveTrackColor, + end: sliderTheme.activeTrackColor, + ); + final inactiveTrackColorTween = ColorTween( + begin: sliderTheme.disabledInactiveTrackColor, + end: showScale ? trackColorWhenShowScale : sliderTheme.inactiveTrackColor, + ); + final activePaint = Paint() + ..color = activeTrackColorTween.evaluate(enableAnimation)!; + final inactivePaint = Paint() + ..color = inactiveTrackColorTween.evaluate(enableAnimation)!; + + final Offset leftThumbOffset; + final Offset rightThumbOffset; + switch (textDirection) { + case TextDirection.ltr: + leftThumbOffset = startThumbCenter; + rightThumbOffset = endThumbCenter; + break; + case TextDirection.rtl: + leftThumbOffset = endThumbCenter; + rightThumbOffset = startThumbCenter; + break; + } + final thumbSize = + sliderTheme.rangeThumbShape!.getPreferredSize(isEnabled, isDiscrete); + final thumbRadius = thumbSize.width / 2; + assert(thumbRadius > 0); + + final trackRect = getPreferredRect( + parentBox: parentBox, + offset: offset, + sliderTheme: sliderTheme, + isEnabled: isEnabled, + isDiscrete: isDiscrete, + ); + themeData.sliderMeasureData.trackerRect = trackRect; + + final trackRadius = Radius.circular(trackRect.height / 2); + final padding = extraPadding(); + + context.canvas.drawRRect( + RRect.fromLTRBAndCorners( + trackRect.left - padding, + trackRect.top, + trackRect.right + padding, + trackRect.bottom, + topLeft: trackRadius, + bottomLeft: trackRadius, + topRight: trackRadius, + bottomRight: trackRadius, + ), + inactivePaint, + ); + var activeTrackRadius = + Radius.circular(trackRect.height / 2 - additionalActiveTrackHeight); + final inactiveSecondPaint = Paint() + ..color = sliderTheme.inactiveTrackColor!; + if (showScale) { + context.canvas.drawRRect( + RRect.fromLTRBAndCorners( + trackRect.left + additionalActiveTrackHeight, + trackRect.top + additionalActiveTrackHeight, + rightThumbOffset.dx, + trackRect.bottom - additionalActiveTrackHeight, + topLeft: activeTrackRadius, + bottomLeft: activeTrackRadius, + ), + inactiveSecondPaint, + ); + } + context.canvas.drawRect( + Rect.fromLTRB( + leftThumbOffset.dx, + trackRect.top + additionalActiveTrackHeight, + rightThumbOffset.dx, + trackRect.bottom - additionalActiveTrackHeight, + ), + activePaint, + ); + if (themeData.showScaleValue) { + context.canvas.drawRRect( + RRect.fromLTRBAndCorners( + rightThumbOffset.dx, + trackRect.top + additionalActiveTrackHeight, + trackRect.right - additionalActiveTrackHeight, + trackRect.bottom - additionalActiveTrackHeight, + topRight: activeTrackRadius, + bottomRight: activeTrackRadius, + ), + inactiveSecondPaint, + ); + } + } +} + +/// The default shape of a [RangeSlider]'s thumbs. +/// +/// There is a shadow for the resting and pressed state. +/// +/// ![A slider widget, consisting of 5 divisions and showing the round range slider thumb shape.] +/// (https://flutter.github.io/assets-for-api-docs/assets/material/round_range_slider_thumb_shape.png) +/// +/// See also: +/// +/// * [RangeSlider], which includes thumbs defined by this shape. +/// * [SliderTheme], which can be used to configure the thumb shapes of all +/// range sliders in a widget subtree. +class TDCapsuleRangeSliderThumbShape extends RangeSliderThumbShape with TDCapsuleRectAdjustment { + /// Create a slider thumb that draws a circle. + const TDCapsuleRangeSliderThumbShape({ + this.enabledThumbRadius = 10.0, + this.disabledThumbRadius, + this.elevation = 3.0, + this.pressedElevation = 3.0, + required this.themeData, + }); + + /// The preferred radius of the round thumb shape when the slider is enabled. + /// + /// If it is not provided, then the Material Design default of 10 is used. + final double enabledThumbRadius; + + /// The preferred radius of the round thumb shape when the slider is disabled. + /// + /// If no disabledRadius is provided, then it is equal to the + /// [enabledThumbRadius]. + final double? disabledThumbRadius; + + double get _disabledThumbRadius => disabledThumbRadius ?? enabledThumbRadius; + + /// The resting elevation adds shadow to the unpressed thumb. + /// + /// The default is 1. + final double elevation; + + /// The pressed elevation adds shadow to the pressed thumb. + /// + /// The default is 6. + final double pressedElevation; + + @override + final TDSliderThemeData themeData; + + @override + Size getPreferredSize(bool isEnabled, bool isDiscrete) { + return Size.fromRadius( + isEnabled == true ? enabledThumbRadius : _disabledThumbRadius); + } + + @override + void paint( + PaintingContext context, + Offset center, { + required Animation activationAnimation, + required Animation enableAnimation, + bool isDiscrete = false, + bool isEnabled = false, + bool? isOnTop, + required SliderThemeData sliderTheme, + TextDirection? textDirection, + Thumb? thumb, + bool? isPressed, + }) { + assert(sliderTheme.showValueIndicator != null); + assert(sliderTheme.overlappingShapeStrokeColor != null); + final canvas = context.canvas; + final radiusTween = Tween( + begin: _disabledThumbRadius, + end: enabledThumbRadius, + ); + final colorTween = ColorTween( + begin: sliderTheme.disabledThumbColor, + end: sliderTheme.thumbColor, + ); + final radius = radiusTween.evaluate(enableAnimation); + final elevationTween = Tween( + begin: elevation, + end: pressedElevation, + ); + + // Add a stroke of 1dp around the circle if this thumb would overlap + // the other thumb. + if (isOnTop ?? false) { + final strokePaint = Paint() + ..color = sliderTheme.overlappingShapeStrokeColor! + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke; + canvas.drawCircle(center, radius, strokePaint); + } + + final color = colorTween.evaluate(enableAnimation)!; + + final evaluatedElevation = + isPressed! ? elevationTween.evaluate(activationAnimation) : elevation; + final shadowPath = Path() + ..addArc( + Rect.fromCenter( + center: center, width: 2 * radius, height: 2 * radius), + 0, + math.pi * 2); + + var paintShadows = true; + if (paintShadows) { + canvas.drawShadow(shadowPath, const Color.fromRGBO(0, 0, 0, 0.5), + evaluatedElevation, true); + } + if (themeData.showThumbValue && + themeData.sliderMeasureData.trackerRect != null) { + var trackerRect = themeData.sliderMeasureData.trackerRect!; + final padding = trackPadding(); + var ratio = (center.dx - trackerRect.left - padding) / (trackerRect.right - trackerRect.left - padding * 2); + //计算滑块的值 + var value = (themeData.max - themeData.min) * ratio + themeData.min; + //格式化显示 + var formatterValue = themeData.scaleFormatter == null + ? value.toStringAsFixed(2) + : themeData.scaleFormatter!(value); + //绘制数值 + var painter = TextPainter( + text: TextSpan( + text: '$formatterValue', style: themeData.thumbTextStyle), + textDirection: TextDirection.ltr, + textAlign: TextAlign.center) + ..layout(maxWidth: 100); + painter.paint( + context.canvas, + Offset(center.dx - painter.size.width / 2, + center.dy - painter.height - 16)); + } + + //绘制游标 + canvas.drawCircle( + center, + radius, + Paint()..color = color, + ); + } +} + +/// The default shape of each [RangeSlider] tick mark. +/// +/// Tick marks are only displayed if the slider is discrete, which can be done +/// by setting the [RangeSlider.divisions] to an integer value. +/// +/// It paints a solid circle, centered on the track. +/// The color is determined by the [Slider]'s enabled state and track's active +/// states. These colors are defined in: +/// [SliderThemeData.activeTrackColor], +/// [SliderThemeData.inactiveTrackColor], +/// [SliderThemeData.disabledActiveTrackColor], +/// [SliderThemeData.disabledInactiveTrackColor]. +/// +/// ![A slider widget, consisting of 5 divisions and showing the round range slider tick mark shape.] +/// (https://flutter.github.io/assets-for-api-docs/assets/material/round_range_slider_tick_mark_shape.png ) +/// +/// See also: +/// +/// * [RangeSlider], which includes tick marks defined by this shape. +/// * [SliderTheme], which can be used to configure the tick mark shape of all +/// sliders in a widget subtree. +class TDCapsuleRangeSliderTickMarkShape extends RangeSliderTickMarkShape { + /// Create a range slider tick mark that draws a circle. + const TDCapsuleRangeSliderTickMarkShape({ + this.tickMarkRadius, + required this.themeData, + }); + + /// The preferred radius of the round tick mark. + /// + /// If it is not provided, then 1/4 of the [SliderThemeData.trackHeight] is used. + final double? tickMarkRadius; + + final TDSliderThemeData themeData; + + @override + Size getPreferredSize({ + required SliderThemeData sliderTheme, + bool isEnabled = false, + }) { + return Size.fromRadius(tickMarkRadius ?? 4); + } + + @override + void paint( + PaintingContext context, + Offset center, { + required RenderBox parentBox, + required SliderThemeData sliderTheme, + required Animation enableAnimation, + required Offset startThumbCenter, + required Offset endThumbCenter, + bool isEnabled = false, + required TextDirection textDirection, + }) { + assert(sliderTheme.disabledActiveTickMarkColor != null); + assert(sliderTheme.disabledInactiveTickMarkColor != null); + assert(sliderTheme.activeTickMarkColor != null); + assert(sliderTheme.inactiveTickMarkColor != null); + + // The tick marks are tiny circles that are the same height as the track. + final tickMarkRadius = getPreferredSize( + isEnabled: isEnabled, + sliderTheme: sliderTheme, + ).width / + 2; + var dx = center.dx; + if (tickMarkRadius > 0 && themeData.showScaleValue) { + assert(themeData.divisions != null); + var rect = sliderTheme.rangeTrackShape + ?.getPreferredRect(parentBox: parentBox, sliderTheme: sliderTheme); + if (rect != null && themeData.divisions! > 0) { + //轨道的高度 + var trackHeight = rect.bottom - rect.top; + //最左边的刻度中心到最右边刻度中心的长度 + var markWidth = (rect.right - rect.left) - trackHeight; + //最左边刻度的起点 + var markStart = rect.left + trackHeight / 2; + //每个刻度的宽度 + var perWidth = markWidth / themeData.divisions!; + assert(perWidth > 0); + //计算当前是第几个刻度 + var index = ((center.dx - markStart) / perWidth).round(); + //获取当前刻度的值 + var value = themeData.min + + index * ((themeData.max - themeData.min) / themeData.divisions!); + //格式化数值 + var valueFormatter = themeData.scaleFormatter != null + ? themeData.scaleFormatter!(value) + : value.toString(); + //修正x坐标 + dx = rect.left + + index * (((rect.right - rect.left) / themeData.divisions!)); + //绘制刻度的值 + var painter = TextPainter( + text: TextSpan( + text: valueFormatter, + style: enableAnimation.value > 0 + ? themeData.scaleTextStyle + : themeData.disabledScaleTextStyle), + textDirection: TextDirection.ltr, + textAlign: TextAlign.center) + ..layout(maxWidth: 100); + var x = dx - painter.size.width / 2; + painter.paint(context.canvas, Offset(x, center.dy - painter.height - 16)); + + // 第一个和最后一个不展示 + if (index > 0 && index < themeData.divisions!) { + final bool isBetweenThumbs; + switch (textDirection) { + case TextDirection.ltr: + isBetweenThumbs = startThumbCenter.dx < center.dx && center.dx < endThumbCenter.dx; + break; + case TextDirection.rtl: + isBetweenThumbs = endThumbCenter.dx < center.dx && center.dx < startThumbCenter.dx; + break; + } + final begin = + isBetweenThumbs ? sliderTheme.disabledActiveTickMarkColor : sliderTheme.disabledInactiveTickMarkColor; + final end = isBetweenThumbs ? sliderTheme.activeTickMarkColor : sliderTheme.inactiveTickMarkColor; + final paint = Paint() + ..strokeWidth = 2 + ..color = ColorTween(begin: begin, end: end).evaluate(enableAnimation)!; + context.canvas.drawLine(Offset(dx, themeData.sliderMeasureData.trackerRect!.top + 3), + Offset(dx, themeData.sliderMeasureData.trackerRect!.bottom - 3), paint); + } + } + } + } +} diff --git a/tdesign-component/lib/src/components/stepper/td_stepper.dart b/tdesign-component/lib/src/components/stepper/td_stepper.dart new file mode 100644 index 000000000..9b427d9b8 --- /dev/null +++ b/tdesign-component/lib/src/components/stepper/td_stepper.dart @@ -0,0 +1,475 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../../tdesign_flutter.dart'; + +enum TDStepperSize { small, medium, large } + +enum TDStepperTheme { normal, filled, outline } + +enum TDStepperIconType { remove, add } + +enum TDStepperOverlimitType { minus, plus } + +enum TDStepperEventType { cleanValue } + +typedef TDStepperOverlimitFunction = void Function(TDStepperOverlimitType type); + +class TDStepper extends StatefulWidget { + const TDStepper({ + Key? key, + this.disableInput = false, + this.disabled = false, + this.inputWidth, + this.eventController, + this.max = 100, + this.min = 0, + this.size = TDStepperSize.medium, + this.step = 1, + this.theme = TDStepperTheme.normal, + this.value = 0, + this.defaultValue = 0, + this.onBlur, + this.onChange, + this.onOverlimit, + }) : super(key: key); + + /// 禁用输入框 + final bool disableInput; + + /// 禁用全部操作 + final bool disabled; + + /// 禁用全部操作 + final double? inputWidth; + + /// 最大值 + final int max; + + /// 最小值 + final int min; + + /// 组件尺寸 + final TDStepperSize size; + + /// 步长 + final int step; + + /// 组件风格 + final TDStepperTheme theme; + + /// 值 + final int? value; + + /// 默认值 + final int? defaultValue; + + /// 输入框失去焦点时触发 + final VoidCallback? onBlur; + + /// 数值发生变更时触发 + final ValueChanged? onChange; + + /// 数值超出限制时触发 + final TDStepperOverlimitFunction? onOverlimit; + + /// 事件控制器 + final StreamController? eventController; + @override + State createState() => _TDStepperState(); +} + +class _TDStepperState extends State { + late int value; + late TextEditingController _controller; + final FocusNode _focusNode = FocusNode(); + + @override + void initState() { + super.initState(); + value = widget.value ?? widget.defaultValue ?? 0; + if(widget.eventController!=null){ + widget.eventController?.stream.listen((TDStepperEventType event){ + if(event == TDStepperEventType.cleanValue){ + cleanValue(); + } + }); + } + _controller = TextEditingController(text: value.toString()); + + _focusNode.addListener(() { + if (!_focusNode.hasFocus) { + if (widget.onBlur != null) { + widget.onBlur!(); + } + } + }); + } + + @override + void dispose() { + _controller.dispose(); + _focusNode.dispose(); + super.dispose(); + } + + double _getWidth() { + if (widget.inputWidth != null && widget.inputWidth! > 0) { + return widget.inputWidth!; + } + + switch (widget.size) { + case TDStepperSize.small: + return 34; + case TDStepperSize.medium: + return 38; + case TDStepperSize.large: + return 45; + default: + return 38; + } + } + + double _getTextWidth() { + var textLength = value.toString().length; + return textLength < 4 ? 0 : (textLength - 4) * _getFontSize(); + } + + double _getHeight() { + switch (widget.size) { + case TDStepperSize.small: + return 20; + case TDStepperSize.medium: + return 24; + case TDStepperSize.large: + return 28; + default: + return 24; + } + } + + Color? _getBackgroundColor(BuildContext context) { + switch (widget.theme) { + case TDStepperTheme.filled: + return widget.disabled + ? TDTheme.of(context).grayColor2 + : TDTheme.of(context).grayColor1; + case TDStepperTheme.outline: + return TDTheme.of(context).whiteColor1; + case TDStepperTheme.normal: + default: + return null; + } + } + + double _getFontSize() { + switch (widget.size) { + case TDStepperSize.small: + return 10; + case TDStepperSize.medium: + return 12; + case TDStepperSize.large: + return 16; + default: + return 12; + } + } + + void onAdd() { + if (value >= widget.max) { + return; + } + + if (value + widget.step > widget.max) { + setState(() { + value = widget.max; + }); + + if (widget.onOverlimit != null) { + widget.onOverlimit!(TDStepperOverlimitType.plus); + } + + renderNumber(); + return; + } + + setState(() { + value += widget.step; + }); + + renderNumber(); + } + + void onReduce() { + if (value <= widget.min) { + return; + } + + if (value - widget.step < widget.min) { + setState(() { + value = widget.min; + }); + + if (widget.onOverlimit != null) { + widget.onOverlimit!(TDStepperOverlimitType.minus); + } + + renderNumber(); + return; + } + + setState(() { + value -= widget.step; + }); + renderNumber(); + } + cleanValue(){ + value=0; + _controller.value = TextEditingValue( + text: value.toString(), + selection: TextSelection.fromPosition(TextPosition( + affinity: TextAffinity.downstream, + offset: value.toString().length, + ))); + _focusNode.unfocus(); + } + void renderNumber() { + _controller.value = TextEditingValue( + text: value.toString(), + selection: TextSelection.fromPosition(TextPosition( + affinity: TextAffinity.downstream, + offset: value.toString().length, + ))); + _focusNode.unfocus(); + + if (widget.onChange != null) { + widget.onChange!(value); + } + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + TDStepperIconButton( + type: TDStepperIconType.remove, + disabled: widget.disabled || value <= widget.min, + theme: widget.theme, + size: widget.size, + onTap: onReduce, + ), + Container( + decoration: BoxDecoration( + border: widget.theme == TDStepperTheme.outline + ? Border( + top: BorderSide( + color: TDTheme.of(context).grayColor4, + ), + bottom: BorderSide( + color: TDTheme.of(context).grayColor4, + )) + : null), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: widget.theme == TDStepperTheme.normal ? 0 : 4), + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: _getWidth(), + maxWidth: _getWidth() + _getTextWidth()), + child: Container( + height: _getHeight(), + alignment: Alignment.center, + decoration: + BoxDecoration(color: _getBackgroundColor(context)), + child: Container( + height: PlatformUtil.isWeb ? _getFontSize() : null, + padding: const EdgeInsets.symmetric(vertical: 2), + child: TextField( + controller: _controller, + enabled: !widget.disabled && !widget.disableInput, + focusNode: _focusNode, + style: TextStyle( + fontSize: _getFontSize(), + color: widget.disabled + ? TDTheme.of(context).fontGyColor4 + : TDTheme.of(context).fontGyColor1), + textAlign: TextAlign.center, + textAlignVertical: TextAlignVertical.center, + keyboardType: TextInputType.number, + decoration: InputDecoration( + border: InputBorder.none, + isDense: true, + contentPadding: EdgeInsets.zero, + ), + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + TextInputFormatter.withFunction((oldValue, newValue) { + try { + if (newValue.text == '') { + setState(() { + value = widget.min; + }); + + if (widget.onOverlimit != null) { + widget + .onOverlimit!(TDStepperOverlimitType.minus); + } + + return newValue.copyWith( + text: value.toString(), + selection: TextSelection.collapsed( + offset: value.toString().length)); + } + + final newNum = int.parse(newValue.text); + if (newNum < widget.min) { + setState(() { + value = widget.min; + }); + if (widget.onOverlimit != null) { + widget + .onOverlimit!(TDStepperOverlimitType.minus); + } + } else if (newNum > widget.max) { + setState(() { + value = widget.max; + }); + if (widget.onOverlimit != null) { + widget + .onOverlimit!(TDStepperOverlimitType.plus); + } + } else { + setState(() { + value = newNum; + }); + } + + return newValue.copyWith( + text: value.toString(), + selection: TextSelection.collapsed( + offset: value.toString().length)); + } catch (e) { + return oldValue; + } + }) + ], + onChanged: (newValue) { + final result = int.parse(newValue); + if (widget.onChange != null) { + widget.onChange!(result); + } + }, + ), + ), + ), + )), + ), + TDStepperIconButton( + type: TDStepperIconType.add, + disabled: widget.disabled || value >= widget.max, + theme: widget.theme, + size: widget.size, + onTap: onAdd, + ) + ], + ); + } +} + +typedef TDTapFunction = void Function(); + +class TDStepperIconButton extends StatelessWidget { + const TDStepperIconButton( + {Key? key, + this.onTap, + this.size = TDStepperSize.medium, + this.disabled = false, + this.theme = TDStepperTheme.normal, + required this.type}) + : super(key: key); + + final TDTapFunction? onTap; + final TDStepperSize size; + final TDStepperIconType type; + final bool disabled; + final TDStepperTheme theme; + + double _getIconSize() { + switch (size) { + case TDStepperSize.large: + return 20; + case TDStepperSize.medium: + return 16; + case TDStepperSize.small: + return 12; + default: + return 16; + } + } + + Icon _getIcon(context) { + var iconType = type == TDStepperIconType.add ? Icons.add : Icons.remove; + + return Icon(iconType, + size: _getIconSize(), + color: disabled + ? TDTheme.of(context).fontGyColor4 + : TDTheme.of(context).fontGyColor1); + } + + Color? _getBackgroundColor(BuildContext context) { + switch (theme) { + case TDStepperTheme.filled: + return disabled + ? TDTheme.of(context).grayColor2 + : TDTheme.of(context).grayColor1; + case TDStepperTheme.outline: + return disabled ? TDTheme.of(context).grayColor2 : null; + case TDStepperTheme.normal: + default: + return null; + } + } + + BorderRadiusGeometry? _getBorderRadius(BuildContext context) { + if (theme == TDStepperTheme.normal) { + return null; + } + + return type == TDStepperIconType.remove + ? const BorderRadius.only( + topLeft: Radius.circular(3), bottomLeft: Radius.circular(3)) + : const BorderRadius.only( + topRight: Radius.circular(3), bottomRight: Radius.circular(3)); + } + + BoxBorder? _getBoxBorder(BuildContext context) { + if (theme == TDStepperTheme.outline) { + return Border.all( + color: TDTheme.of(context).grayColor4, + ); + } + + return null; + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: disabled ? null : onTap, + child: Container( + decoration: BoxDecoration( + color: _getBackgroundColor(context), + borderRadius: _getBorderRadius(context), + border: _getBoxBorder(context), + ), + child: Padding( + padding: const EdgeInsets.all(4.0), + child: _getIcon(context), + ), + )); + } +} + diff --git a/tdesign-component/lib/src/components/steps/td_steps.dart b/tdesign-component/lib/src/components/steps/td_steps.dart new file mode 100644 index 000000000..2fa4bea6b --- /dev/null +++ b/tdesign-component/lib/src/components/steps/td_steps.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/src/components/steps/td_steps_horizontal.dart'; +import 'package:tdesign_flutter/src/components/steps/td_steps_vertical.dart'; +import '../../../tdesign_flutter.dart'; + +/// Steps步骤条数据类型 +class TDStepsItemData { + TDStepsItemData( + {this.title, + this.content, + this.successIcon, + this.errorIcon, + this.customContent, + this.customTitle}) { + _validate(); + } + + /// 标题 + final String? title; + + /// 内容 + final String? content; + + /// 成功图标 + final IconData? successIcon; + + /// 失败图标 + final IconData? errorIcon; + + /// 自定义内容 + final Widget? customContent; + + /// 自定义标题 + final Widget? customTitle; + + /// 校验参数 + void _validate() { + if (title == null && + customTitle == null && + content == null && + customContent == null) { + throw ArgumentError( + 'title, content, customContent needs at least one non-empty value'); + } + } +} + +/// Steps步骤条方向 +enum TDStepsDirection { + horizontal, + vertical, +} + +/// steps步骤条状态 +enum TDStepsStatus { + success, + error, +} + +/// Steps步骤条 +class TDSteps extends StatefulWidget { + const TDSteps({ + super.key, + required this.steps, + this.activeIndex = 0, + this.direction = TDStepsDirection.horizontal, + this.status = TDStepsStatus.success, + this.simple = false, + this.readOnly = false, + this.verticalSelect = false, + }); + + /// 步骤条数据 + final List steps; + + /// 步骤条方向 + final TDStepsDirection direction; + + /// 步骤条当前激活的索引 + final int activeIndex; + + /// 步骤条状态 + final TDStepsStatus status; + + /// 步骤条simple模式 + final bool simple; + + /// 步骤条readOnly模式 + final bool readOnly; + + /// 步骤条垂直自定义步骤条选择模式 + final bool verticalSelect; + + @override + _TDStepsState createState() => _TDStepsState(); +} + +class _TDStepsState extends State { + @override + Widget build(BuildContext context) { + /// 当前激活的step索引 + final currentActiveIndex = widget.activeIndex < 0 + ? 0 + : (widget.activeIndex >= widget.steps.length + ? widget.steps.length - 1 + : widget.activeIndex); + return widget.direction == TDStepsDirection.horizontal + ? TDStepsHorizontal( + steps: widget.steps, + activeIndex: currentActiveIndex, + status: widget.status, + simple: widget.simple, + readOnly: widget.readOnly) + : TDStepsVertical( + steps: widget.steps, + activeIndex: currentActiveIndex, + status: widget.status, + simple: widget.simple, + readOnly: widget.readOnly, + verticalSelect: widget.verticalSelect, + ); + } +} diff --git a/tdesign-component/lib/src/components/steps/td_steps_horizontal.dart b/tdesign-component/lib/src/components/steps/td_steps_horizontal.dart new file mode 100644 index 000000000..442f76a92 --- /dev/null +++ b/tdesign-component/lib/src/components/steps/td_steps_horizontal.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/src/components/steps/td_steps_horizontal_item.dart'; +import '../../../tdesign_flutter.dart'; + +/// Steps步骤条,水平步骤 +class TDStepsHorizontal extends StatelessWidget { + final List steps; + final int activeIndex; + final TDStepsStatus status; + final bool simple; + final bool readOnly; + const TDStepsHorizontal({ + super.key, + required this.steps, + required this.activeIndex, + required this.status, + required this.simple, + required this.readOnly, + }); + + @override + Widget build(BuildContext context) { + final stepsCount = steps.length; + List stepsHorizontalItem = steps.asMap().entries.map((item){ + return Expanded( + flex: 1, + child: TDStepsHorizontalItem( + index: item.key, + data: item.value, + stepsCount: stepsCount, + activeIndex: activeIndex, + status: status, + simple: simple, + readOnly: readOnly, + ), + ); + }).toList(); + + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: stepsHorizontalItem, + ); + } + +} + diff --git a/tdesign-component/lib/src/components/steps/td_steps_horizontal_item.dart b/tdesign-component/lib/src/components/steps/td_steps_horizontal_item.dart new file mode 100644 index 000000000..e1a845e6c --- /dev/null +++ b/tdesign-component/lib/src/components/steps/td_steps_horizontal_item.dart @@ -0,0 +1,239 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +/// Steps步骤条,水平步骤item +class TDStepsHorizontalItem extends StatelessWidget { + final TDStepsItemData data; + final int index; + final int stepsCount; + final int activeIndex; + final TDStepsStatus status; + final bool simple; + final bool readOnly; + + const TDStepsHorizontalItem({ + super.key, + required this.data, + required this.index, + required this.stepsCount, + required this.activeIndex, + required this.status, + required this.simple, + required this.readOnly, + }); + + @override + Widget build(BuildContext context) { + /// 步骤条数字背景色 + var stepsNumberBgColor = TDTheme.of(context).brandNormalColor; + + /// 步骤条数字颜色 + var stepsNumberTextColor = TDTheme.of(context).whiteColor1; + + /// 步骤条标题颜色 + var stepsTitleColor = TDTheme.of(context).brandColor7; + + /// 步骤条icon颜色 + var stepsIconColor = TDTheme.of(context).brandColor7; + + /// 简略步骤条icon颜色 + var simpleStepsIconColor = TDTheme.of(context).brandColor7; + + /// 是否要设置步骤图标widget的Decoration + bool shouldSetIconWidgetDecoration = true; + + Widget? completeIconWidget; + + /// 错误icon图标显示 + Widget errorIconWidget = Icon( + TDIcons.close, + color: TDTheme.of(context).errorColor6, + size: 16, + ); + + /// 激活索引大于当前索引 + if (activeIndex > index) { + stepsNumberBgColor = TDTheme.of(context).brandColor1; + stepsNumberTextColor = TDTheme.of(context).brandColor7; + stepsTitleColor = TDTheme.of(context).fontGyColor1; + + /// 已完成的用icon图标显示 + completeIconWidget = Icon( + TDIcons.check, + color: TDTheme.of(context).brandColor7, + size: 16, + ); + } else if (activeIndex < index) { + /// 激活索引小于当前索引 + stepsNumberBgColor = TDTheme.of(context).grayColor1; + stepsNumberTextColor = TDTheme.of(context).fontGyColor3; + stepsTitleColor = TDTheme.of(context).fontGyColor3; + stepsIconColor = TDTheme.of(context).fontGyColor3; + simpleStepsIconColor = TDTheme.of(context).grayColor4; + } + + /// 步骤条icon图标组件,默认为索引文字 + Widget? stepsIconWidget = Text( + (index + 1).toString(), + style: TextStyle( + color: stepsNumberTextColor, + fontWeight: FontWeight.w400, + fontSize: 14, + ), + ); + + /// 已完成的用icon图标显示 + if (completeIconWidget != null) { + stepsIconWidget = completeIconWidget; + } + + /// 传递了成功的icon图标, 已完成的step都需要显示 + if (data.successIcon != null) { + stepsIconWidget = Icon( + data.successIcon, + color: stepsIconColor, + size: 22, + ); + + /// 传了图标则不用设置背景色 + shouldSetIconWidgetDecoration = false; + } + + /// 状态是错误状态,激活索引是当前索引,只有当前激活索引才需要显示 + if (status == TDStepsStatus.error && activeIndex == index) { + /// 显示错误颜色 + stepsNumberBgColor = TDTheme.of(context).errorColor1; + stepsTitleColor = TDTheme.of(context).errorColor6; + + /// 显示错误图标 + stepsIconWidget = errorIconWidget; + if (data.errorIcon != null) { + stepsIconWidget = Icon( + data.errorIcon, + color: TDTheme.of(context).errorColor6, + size: 22, + ); + } + + /// 传了图标则不用设置背景色等Decoration + shouldSetIconWidgetDecoration = data.errorIcon == null; + if (simple) { + simpleStepsIconColor = TDTheme.of(context).errorColor6; + } + } + + /// 步骤条icon图标背景和形状 + BoxDecoration? iconWidgetDecoration = shouldSetIconWidgetDecoration + ? BoxDecoration( + color: stepsNumberBgColor, + shape: BoxShape.circle, + ) + : null; + + /// icon组件容器大小 + double iconContainerSize = 22; + + /// 简略步骤条 + if (simple || readOnly) { + /// readOnly纯展示 + if (readOnly) { + simpleStepsIconColor = TDTheme.of(context).brandColor7; + stepsTitleColor = TDTheme.of(context).fontGyColor1; + } + iconContainerSize = 8; + stepsIconWidget = null; + + /// 简略步骤条BoxDecoration + var simpleDecoration = BoxDecoration( + color: Colors.transparent, + shape: BoxShape.circle, + border: Border.all( + color: simpleStepsIconColor, + width: 1, + ), + ); + if (activeIndex == index && !readOnly) { + simpleDecoration = BoxDecoration( + color: simpleStepsIconColor, + shape: BoxShape.circle, + ); + } + iconWidgetDecoration = simpleDecoration; + } + + return Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + flex: 1, + child: Opacity( + opacity: index == 0 ? 0 : 1, + child: Container( + width: double.infinity, + height: 1, + color: (activeIndex >= index || readOnly) + ? TDTheme.of(context).brandColor7 + : TDTheme.of(context).grayColor4), + ), + ), + Container( + width: iconContainerSize, + height: iconContainerSize, + alignment: Alignment.center, + margin: const EdgeInsets.only(left: 8, right: 8), + decoration: iconWidgetDecoration, + child: stepsIconWidget, + ), + Expanded( + flex: 1, + child: Opacity( + opacity: index == stepsCount - 1 ? 0 : 1, + child: Container( + width: double.infinity, + height: 1, + color: (activeIndex > index || readOnly) + ? TDTheme.of(context).brandColor7 + : TDTheme.of(context).grayColor4, + ), + ), + ), + ], + ), + if (data.customTitle != null) + data.customTitle! + else if (data.title != null && data.title!.isNotEmpty) + Container( + margin: const EdgeInsets.only(top: 8), + alignment: Alignment.center, + child: TDText( + data.title!, + style: TextStyle( + fontWeight: (activeIndex == index && !readOnly) + ? FontWeight.w600 + : FontWeight.w400, + color: stepsTitleColor, + fontSize: 14, + ), + ), + ), + Container( + margin: const EdgeInsets.only(top: 4), + alignment: Alignment.center, + child: data.customContent ?? + TDText( + data.content ?? '', + style: TextStyle( + fontWeight: FontWeight.w400, + color: TDTheme.of(context).fontGyColor3, + fontSize: 12, + ), + ), + ), + ], + ); + } +} diff --git a/tdesign-component/lib/src/components/steps/td_steps_vertical.dart b/tdesign-component/lib/src/components/steps/td_steps_vertical.dart new file mode 100644 index 000000000..6bcb4618b --- /dev/null +++ b/tdesign-component/lib/src/components/steps/td_steps_vertical.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:tdesign_flutter/src/components/steps/td_steps_vertical_item.dart'; +import '../../../tdesign_flutter.dart'; + +/// Steps步骤条,垂直步骤 +class TDStepsVertical extends StatelessWidget { + final List steps; + final int activeIndex; + final TDStepsStatus status; + final bool simple; + final bool readOnly; + final bool verticalSelect; + const TDStepsVertical({ + super.key, + required this.steps, + required this.activeIndex, + required this.status, + required this.simple, + required this.readOnly, + required this.verticalSelect, + }); + + @override + Widget build(BuildContext context) { + final stepsCount = steps.length; + List stepsVerticalItem = steps.asMap().entries.map((item){ + return TDStepsVerticalItem( + index: item.key, + data: item.value, + stepsCount: stepsCount, + activeIndex: activeIndex, + status: status, + simple: simple, + readOnly: readOnly, + verticalSelect: verticalSelect, + ); + }).toList(); + + return Column( + mainAxisAlignment: MainAxisAlignment.start, + children: stepsVerticalItem, + ); + } + +} + diff --git a/tdesign-component/lib/src/components/steps/td_steps_vertical_item.dart b/tdesign-component/lib/src/components/steps/td_steps_vertical_item.dart new file mode 100644 index 000000000..117412913 --- /dev/null +++ b/tdesign-component/lib/src/components/steps/td_steps_vertical_item.dart @@ -0,0 +1,285 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +/// Steps步骤条,垂直步骤item +class TDStepsVerticalItem extends StatelessWidget { + final TDStepsItemData data; + final int index; + final int stepsCount; + final int activeIndex; + final TDStepsStatus status; + final bool simple; + final bool readOnly; + final bool verticalSelect; + + ///item 标题组件插槽 + final Widget? titleWidget; + + const TDStepsVerticalItem( + {super.key, + required this.data, + required this.index, + required this.stepsCount, + required this.activeIndex, + required this.status, + required this.simple, + required this.readOnly, + required this.verticalSelect, + this.titleWidget}); + + @override + Widget build(BuildContext context) { + /// 步骤条数字背景色 + var stepsNumberBgColor = TDTheme.of(context).brandNormalColor; + + /// 步骤条数字颜色 + var stepsNumberTextColor = TDTheme.of(context).whiteColor1; + + /// 步骤条标题颜色 + var stepsTitleColor = TDTheme.of(context).brandColor7; + + /// 步骤条icon颜色 + var stepsIconColor = TDTheme.of(context).brandColor7; + + /// 简略步骤条icon颜色 + var simpleStepsIconColor = TDTheme.of(context).brandColor7; + + /// 是否要设置步骤图标widget的Decoration + bool shouldSetIconWidgetDecoration = true; + + Widget? completeIconWidget; + + /// 错误icon图标显示 + Widget errorIconWidget = Icon( + TDIcons.close, + color: TDTheme.of(context).errorColor6, + size: 16, + ); + + /// 激活索引大于当前索引 + if (activeIndex > index) { + stepsNumberBgColor = TDTheme.of(context).brandColor1; + stepsNumberTextColor = TDTheme.of(context).brandColor7; + stepsTitleColor = TDTheme.of(context).fontGyColor1; + + /// 已完成的用icon图标显示 + completeIconWidget = Icon( + TDIcons.check, + color: TDTheme.of(context).brandColor7, + size: 16, + ); + } else if (activeIndex < index) { + /// 激活索引小于当前索引 + stepsNumberBgColor = TDTheme.of(context).grayColor1; + stepsNumberTextColor = TDTheme.of(context).fontGyColor3; + stepsTitleColor = TDTheme.of(context).fontGyColor3; + stepsIconColor = TDTheme.of(context).fontGyColor3; + simpleStepsIconColor = TDTheme.of(context).grayColor4; + } + + /// 步骤条icon图标组件,默认为索引文字 + Widget? stepsIconWidget = Text( + (index + 1).toString(), + style: TextStyle( + color: stepsNumberTextColor, + fontWeight: FontWeight.w400, + fontSize: 14, + ), + ); + + /// 已完成的用icon图标显示 + if (completeIconWidget != null) { + stepsIconWidget = completeIconWidget; + } + + /// 传递了成功的icon图标, 已完成的step都需要显示 + if (data.successIcon != null) { + stepsIconWidget = Icon( + data.successIcon, + color: stepsIconColor, + size: 22, + ); + + /// 传了图标则不用设置背景色 + shouldSetIconWidgetDecoration = false; + } + + /// 状态是错误状态,激活索引是当前索引,只有当前激活索引才需要显示 + if (status == TDStepsStatus.error && activeIndex == index) { + /// 显示错误颜色 + stepsNumberBgColor = TDTheme.of(context).errorColor1; + stepsTitleColor = TDTheme.of(context).errorColor6; + + /// 显示错误图标 + stepsIconWidget = errorIconWidget; + if (data.errorIcon != null) { + stepsIconWidget = Icon( + data.errorIcon, + color: TDTheme.of(context).errorColor6, + size: 22, + ); + } + + /// 传了图标则不用设置背景色等Decoration + shouldSetIconWidgetDecoration = data.errorIcon == null; + if (simple) { + simpleStepsIconColor = TDTheme.of(context).errorColor6; + } + } + + /// 步骤条icon图标背景和形状 + var iconWidgetDecoration = shouldSetIconWidgetDecoration + ? BoxDecoration( + color: stepsNumberBgColor, + shape: BoxShape.circle, + ) + : null; + + /// icon组件容器大小 + double iconContainerSize = 22; + + /// icon组件容器margin + double iconMarginBottom = 8; + + /// 简略步骤条 + if (simple || readOnly) { + /// readOnly纯展示 + if (readOnly) { + simpleStepsIconColor = TDTheme.of(context).brandColor7; + stepsTitleColor = TDTheme.of(context).fontGyColor1; + } + iconContainerSize = 8; + iconMarginBottom = 4; + stepsIconWidget = null; + + /// 简略步骤条BoxDecoration + var simpleDecoration = BoxDecoration( + color: Colors.transparent, + shape: BoxShape.circle, + border: Border.all( + color: simpleStepsIconColor, + width: 1, + ), + ); + if (activeIndex == index && !readOnly) { + simpleDecoration = BoxDecoration( + color: simpleStepsIconColor, + shape: BoxShape.circle, + ); + } + iconWidgetDecoration = simpleDecoration; + } + + /// 自定义内容 + var customContent = data.customContent != null + ? Container( + margin: const EdgeInsets.only(top: 4), + child: data.customContent!, + ) + : Container(); + + return Container( + margin: const EdgeInsets.only(bottom: 8), + child: IntrinsicHeight( + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: const EdgeInsets.only(right: 8), + width: 22, + child: ConstrainedBox( + constraints: const BoxConstraints(minHeight: 62), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + width: iconContainerSize, + height: 22, + alignment: Alignment.center, + margin: EdgeInsets.only(bottom: iconMarginBottom), + decoration: iconWidgetDecoration, + child: stepsIconWidget, + ), + Expanded( + flex: 1, + child: Opacity( + opacity: index == stepsCount - 1 ? 0 : 1, + child: Container( + width: 1, + height: double.infinity, + color: (activeIndex > index || readOnly) + ? TDTheme.of(context).brandColor7 + : TDTheme.of(context).grayColor4, + ), + ), + ), + ], + ), + ), + ), + Expanded( + flex: 1, + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (data.customTitle != null) + data.customTitle! + else if (data.title != null && data.title!.isNotEmpty) + Container( + margin: const EdgeInsets.only(bottom: 4), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: TDText( + data.title!, + style: TextStyle( + fontWeight: (activeIndex == index && !readOnly) + ? FontWeight.w600 + : FontWeight.w400, + color: stepsTitleColor, + fontSize: 14, + height: 1.2, + ), + softWrap: true, + overflow: TextOverflow.visible, + ), + ), + verticalSelect + ? Icon( + TDIcons.chevron_right, + color: TDTheme.of(context).fontGyColor1, + size: 16, + ) + : Container(), + ], + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (data.customContent != null) + data.customContent! + else if (data.content != null && data.content!.isNotEmpty) + TDText( + data.content!, + style: TextStyle( + fontWeight: FontWeight.w400, + color: TDTheme.of(context).fontGyColor3, + fontSize: 12, + ), + ), + ], + ), + ], + ), + ) + ], + ), + ), + ); + } +} diff --git a/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell.dart b/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell.dart new file mode 100644 index 000000000..dd74df820 --- /dev/null +++ b/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell.dart @@ -0,0 +1,270 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; + +import '../../util/list_ext.dart'; +import '../cell/td_cell.dart'; +import 'td_swipe_cell_action.dart'; +import 'td_swipe_cell_inherited.dart'; +import 'td_swipe_cell_panel.dart'; + +export 'package:flutter_slidable/flutter_slidable.dart'; + +enum TDSwipeDirection { right, left } + +/// 滑动单元格组件 +class TDSwipeCell extends StatefulWidget { + const TDSwipeCell({ + Key? key, + this.slidableKey, + required this.cell, + this.disabled = false, + this.opened = const [false, false], + this.right, + this.left, + this.onChange, + this.controller, + this.groupTag, + this.closeWhenOpened = true, + this.closeWhenTapped = true, + this.dragStartBehavior = DragStartBehavior.start, + this.direction = Axis.horizontal, + this.duration = const Duration(milliseconds: 200), + }) : super(key: key); + + /// 滑动组件的 Key + final Key? slidableKey; + + /// 单元格 [TDCell] + final Widget cell; + + /// 是否禁用滑动 + final bool? disabled; + + /// 默认打开,[left, right] + final List? opened; + + /// 右侧滑动操作项面板 + final TDSwipeCellPanel? right; + + /// 左侧滑动操作项面板 + final TDSwipeCellPanel? left; + + /// 滑动展开事件 + final Function(TDSwipeDirection direction, bool open)? onChange; + + /// 自定义控制滑动窗口 + final SlidableController? controller; + + /// 组,配置后,[closeWhenOpened]、[closeWhenTapped]才起作用 + final Object? groupTag; + + /// 当同一组([groupTag])中的一个[TDSwipeCell]打开时,是否关闭组中的所有其他[TDSwipeCell] + final bool? closeWhenOpened; + + /// 当同一组([groupTag])中的一个[TDSwipeCell]被点击时,是否应该关闭组中的所有[TDSwipeCell] + /// + /// [cell]组件被点击时必须传递点击事件,执行`TDSwipeCellInherited.of(context)?.cellClick()` + final bool? closeWhenTapped; + + /// 处理拖动开始行为的方式[GestureDetector.dragStartBehavior] + final DragStartBehavior? dragStartBehavior; + + /// 可拖动的方向 + final Axis? direction; + + /// 打开关闭动画时长 + final Duration? duration; + + Duration get getDuration => duration ?? const Duration(milliseconds: 200); + + static final Map> _controllers = {}; + + static void _pushController(SlidableController controller, Object? tag, {bool del = false}) { + if (tag == null) { + return; + } + if (del) { + if (_controllers.keys.contains(tag)) { + _controllers[tag]!.remove(controller); + } + } else { + if (_controllers.keys.contains(tag)) { + if (!_controllers[tag]!.contains(controller)) { + _controllers[tag]!.add(controller); + } + } else { + _controllers[tag] = [controller]; + } + } + } + + /// 根据[groupTag]关闭[TDSwipeCell] + /// + /// current:保留当前不关闭 + static void close(Object? tag, {SlidableController? current}) { + if (tag == null || !_controllers.keys.contains(tag)) { + return; + } + _controllers[tag]!.forEach((element) { + if (element != current) { + element.close(); + } + }); + } + + /// 获取上下文最近的[controller] + static SlidableController? of(BuildContext context) { + return Slidable.of(context); + } + + @override + _TDSwipeCellState createState() => _TDSwipeCellState(); + +} + +class _TDSwipeCellState extends State with TickerProviderStateMixin { + late final SlidableController controller; + final confirmListenable = ValueNotifier(null); + TDSwipeDirection? openDirection; + + @override + void initState() { + super.initState(); + controller = (widget.controller ?? SlidableController(this)) + ..actionPaneType.addListener(_handleActionPanelTypeChanged) + ..animation.addStatusListener((status) { + confirmListenable.value = null; + }); + TDSwipeCell._pushController(controller, widget.groupTag); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + if ((widget.opened?.length ?? 0) > 0 && widget.opened![0] == true) { + controller.openStartActionPane(duration: widget.getDuration); + } + if ((widget.opened?.length ?? 0) > 1 && widget.opened![1] == true) { + controller.openEndActionPane(duration: widget.getDuration); + } + }); + } + + @override + void didUpdateWidget(covariant TDSwipeCell oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.controller != widget.controller) { + controller.actionPaneType.removeListener(_handleActionPanelTypeChanged); + TDSwipeCell._pushController(controller, widget.groupTag, del: true); + controller = (widget.controller ?? SlidableController(this)) + ..actionPaneType.addListener(_handleActionPanelTypeChanged); + TDSwipeCell._pushController(controller, widget.groupTag); + } + } + + @override + void dispose() { + controller.actionPaneType.removeListener(_handleActionPanelTypeChanged); + controller.dispose(); + TDSwipeCell._pushController(controller, widget.groupTag, del: true); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final rightConfirmLength = widget.right?.confirms?.length ?? 0; + final leftConfirmLength = widget.left?.confirms?.length ?? 0; + + final slidable = Slidable( + key: widget.slidableKey ?? UniqueKey(), + closeOnScroll: false, + child: widget.cell, + controller: controller, + enabled: !(widget.disabled ?? false), + groupTag: widget.groupTag, + startActionPane: widget.left?.build(context), + endActionPane: widget.right?.build(context), + dragStartBehavior: widget.dragStartBehavior ?? DragStartBehavior.start, + direction: widget.direction ?? Axis.horizontal, + ); + return TDSwipeCellInherited( + duration: widget.getDuration, + controller: controller, + cellClick: () { + if (widget.closeWhenTapped == true) { + TDSwipeCell.close(widget.groupTag); + } + }, + actionClick: (action) { + final isLeft = openDirection == TDSwipeDirection.left; + final panel = isLeft ? widget.left! : widget.right!; + final index = panel.children.indexOf(action); + final confirm = panel.confirms?.find((element) => element.confirmIndex?.contains(index) == true); + confirmListenable.value = confirm; + return confirm != null; + }, + child: rightConfirmLength > 0 || leftConfirmLength > 0 + ? ValueListenableBuilder( + valueListenable: confirmListenable, + builder: (BuildContext context, value, Widget? child) { + return Stack( + children: [ + slidable, + _confirmWidget(), + ], + ); + }, + ) + : slidable, + ); + } + + Widget _confirmWidget() { + final isHorizontal = widget.direction == Axis.horizontal; + final isLeft = openDirection == TDSwipeDirection.left; + final pane = isLeft ? widget.left : widget.right; + final extentRatio = pane?.extentRatio ?? 0.3; + return Positioned.fill( + child: FractionallySizedBox( + alignment: isHorizontal + ? (isLeft ? Alignment.centerLeft : Alignment.centerRight) + : (isLeft ? Alignment.topCenter : Alignment.bottomCenter), + widthFactor: isHorizontal ? extentRatio : null, + heightFactor: isHorizontal ? null : extentRatio, + child: AnimatedSwitcher( + duration: widget.getDuration, + transitionBuilder: (child, animation) { + return SlideTransition( + child: child, + position: Tween( + begin: isLeft ? const Offset(-1, 0) : const Offset(1, 0), + end: isLeft ? const Offset(0, 0) : const Offset(0, 0), + ).animate(animation), + ); + }, + child: confirmListenable.value ?? const SizedBox.shrink(), + ), + ), + ); + } + + void _handleActionPanelTypeChanged() { + switch (controller.actionPaneType.value) { + case ActionPaneType.none: + widget.onChange?.call(openDirection!, false); + openDirection = null; + break; + case ActionPaneType.start: + if (widget.closeWhenOpened == true) { + TDSwipeCell.close(widget.groupTag, current: controller); + } + openDirection = TDSwipeDirection.left; + widget.onChange?.call(openDirection!, true); + break; + case ActionPaneType.end: + if (widget.closeWhenOpened == true) { + TDSwipeCell.close(widget.groupTag, current: controller); + } + openDirection = TDSwipeDirection.right; + widget.onChange?.call(openDirection!, true); + break; + } + } +} diff --git a/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell_action.dart b/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell_action.dart new file mode 100644 index 000000000..1120ec094 --- /dev/null +++ b/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell_action.dart @@ -0,0 +1,131 @@ +import 'package:flutter/material.dart'; + +import '../../theme/basic.dart'; +import '../../theme/td_colors.dart'; +import '../../theme/td_fonts.dart'; +import '../../theme/td_theme.dart'; +import '../text/td_text.dart'; +import 'td_swipe_cell_inherited.dart'; +import 'td_swipe_cell_panel.dart'; + +/// 滑动单元格操作按钮 +class TDSwipeCellAction extends StatelessWidget { + const TDSwipeCellAction({ + Key? key, + this.flex = 1, + this.backgroundColor, + this.autoClose = true, + this.onPressed, + this.icon, + this.iconColor, + this.iconSize = 18, + this.spacing = 2, + this.label, + this.labelStyle, + this.direction = Axis.horizontal, + this.confirmIndex, + this.builder, + }) : assert((flex ?? 1) > 0, 'flex must be greater than 0'), + assert(icon != null || label != null, 'icon or label must not be null'), + super(key: key); + + /// 宽度占比,默认为 1,[TDSwipeCellPanel.confirms]下无效(失踪占满整个[TDSwipeCellPanel]宽度) + final int? flex; + + /// 背景颜色 + final Color? backgroundColor; + + /// 点击后自动关闭 + final bool? autoClose; + + /// 点击回调 + final void Function(BuildContext context)? onPressed; + + /// 图标 + final IconData? icon; + + /// 图标颜色,默认label字体颜色 + final Color? iconColor; + + /// 图标大小 + final double? iconSize; + + /// 图标和标题的间距 + final double? spacing; + + /// 标题 + final String? label; + + /// 标题样式 + final TextStyle? labelStyle; + + /// 图标和标题的排列方向 + final Axis? direction; + + /// 指定[TDSwipeCellPanel.children]的索引,来打开该[TDSwipeCellAction] + /// [TDSwipeCellPanel.confirms]参数下才配置该参数 + final List? confirmIndex; + + /// 自定义构建 + final WidgetBuilder? builder; + + @override + Widget build(BuildContext context) { + final fontSize = TDTheme.of(context).fontMarkMedium ?? Font(size: 14, lineHeight: 22, fontWeight: FontWeight.w600); + final children = [ + if (icon != null) + Icon( + icon, + size: iconSize ?? 18, + color: labelStyle?.color ?? TDTheme.of(context).fontWhColor1, + ), + if (icon != null && label != null) SizedBox(width: spacing ?? 2), + if (label != null) + Flexible( + child: TDText( + label, + forceVerticalCenter: true, + font: fontSize, + textColor: TDTheme.of(context).fontWhColor1, + style: labelStyle, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + ]; + final child = GestureDetector( + onTap: () { + _handleTap(context); + }, + child: builder?.call(context) ?? + Container( + width: double.infinity, + height: double.infinity, + color: backgroundColor, + child: Flex( + mainAxisAlignment: MainAxisAlignment.center, + direction: direction ?? Axis.horizontal, + children: children, + ), + ), + ); + return confirmIndex?.isNotEmpty == true + ? child + : Expanded( + flex: flex ?? 1, + child: child, + ); + } + + void _handleTap(BuildContext context) { + final swipeInherited = TDSwipeCellInherited.of(context)!; + var openConfirm = swipeInherited.actionClick(this); + if (openConfirm == true) { + return; + } + onPressed?.call(context); + if (autoClose ?? true) { + swipeInherited.controller.close(duration: swipeInherited.duration); + } + } +} diff --git a/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell_inherited.dart b/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell_inherited.dart new file mode 100644 index 000000000..bef815395 --- /dev/null +++ b/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell_inherited.dart @@ -0,0 +1,29 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; + +import 'td_swipe_cell_action.dart'; + +class TDSwipeCellInherited extends InheritedWidget { + const TDSwipeCellInherited({ + Key? key, + required Widget child, + required this.cellClick, + required this.actionClick, + required this.duration, + required this.controller, + }) : super(child: child, key: key); + + final Duration duration; + final void Function() cellClick; + final bool Function(TDSwipeCellAction action) actionClick; + final SlidableController controller; + + @override + bool updateShouldNotify(covariant TDSwipeCellInherited oldWidget) { + return true; + } + + static TDSwipeCellInherited? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } +} diff --git a/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell_panel.dart b/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell_panel.dart new file mode 100644 index 000000000..d12b47374 --- /dev/null +++ b/tdesign-component/lib/src/components/swipe_cell/td_swipe_cell_panel.dart @@ -0,0 +1,139 @@ +import 'package:flutter/material.dart'; + +import './td_swipe_cell.dart'; +import 'td_swipe_cell_action.dart'; + +enum SwipeMotion { + /// 滚动 + scroll, + + /// 揭开 + behind, + + /// 抽屉 + drawer, + + /// 拉伸 + stretch, +} + +/// 滑动单元格操作面板组件 +class TDSwipeCellPanel { + TDSwipeCellPanel({ + this.extentRatio = 0.3, + this.openThreshold, + this.closeThreshold, + this.motionType, + this.dragDismissible = false, + this.dismissThreshold = 0.75, + this.dismissalDuration = const Duration(milliseconds: 300), + this.resizeDuration = const Duration(milliseconds: 300), + this.closeOnCancel = false, + this.confirmDismiss, + this.onDismissed, + required this.children, + this.confirms, + }) : assert( + confirms == null || + confirms.every((item) => + item.confirmIndex != null && + item.confirmIndex!.every((index) => index >= 0 && index < children.length)), + 'Confirms must have a confirmIndex, ' + 'and each confirmIndex in confirms must be within the range of children indices.', + ); + + /// 宽度占比 + final double? extentRatio; + + /// 拖动多少占比触发打开动作,默认 [extentRatio] 的一半 + final double? openThreshold; + + /// 拖动多少占比触发关闭动作,默认 [extentRatio] 的一半 + final double? closeThreshold; + + /// 滑动动画展示方式 + final SwipeMotion? motionType; + + /// 操作组件列表 + final List children; + + /// 二次确认操作组件列表 + final List? confirms; + + /// 是否可通过拖动操作来移除 [TDSwipeCell] 组件 + final bool? dragDismissible; + + /// 滑动到多少比例时,触发移除。dragDismissible为true才有效 + final double? dismissThreshold; + + /// 触发移除的滑动动画时长。dragDismissible为true才有效 + final Duration? dismissalDuration; + + /// 移除动画(高度变为0)时长。dragDismissible为true才有效 + final Duration? resizeDuration; + + /// 移除取消后,是否关闭滑动单元格。dragDismissible为true才有效 + final bool? closeOnCancel; + + /// 移除前回调,可阻止移除。dragDismissible为true才有效 + final Future Function(BuildContext context)? confirmDismiss; + + /// 移除后回调。dragDismissible为true才有效 + final void Function(BuildContext context)? onDismissed; + + Duration get _dismissalDuration => dismissalDuration ?? const Duration(milliseconds: 300); + + Duration get _resizeDuration => resizeDuration ?? const Duration(milliseconds: 300); + + bool get _dragDismissible => dragDismissible ?? false; + + double get _extentRatio => extentRatio ?? 0.3; + + double get _openThreshold => openThreshold ?? (_extentRatio / 2); + + double get _closeThreshold => closeThreshold ?? (_extentRatio / 2); + + ActionPane build(BuildContext context) { + return ActionPane( + extentRatio: _extentRatio, + motion: getMotionWidget(), + openThreshold: _openThreshold, + closeThreshold: _closeThreshold, + children: children, + dragDismissible: _dragDismissible, + dismissible: _dragDismissible + ? DismissiblePane( + closeOnCancel: closeOnCancel ?? false, + dismissThreshold: dismissThreshold ?? 0.75, + dismissalDuration: _dismissalDuration, + resizeDuration: _resizeDuration, + confirmDismiss: () async { + if (confirmDismiss != null) { + return confirmDismiss!(context); + } + return true; + }, + onDismissed: () async { + await TDSwipeCell.of(context)?.close(); + onDismissed?.call(context); + }, + ) + : null, + ); + } + + Widget getMotionWidget() { + switch (motionType) { + case SwipeMotion.scroll: + return const ScrollMotion(); + case SwipeMotion.behind: + return const BehindMotion(); + case SwipeMotion.drawer: + return const DrawerMotion(); + case SwipeMotion.stretch: + return const StretchMotion(); + default: + return const ScrollMotion(); + } + } +} diff --git a/tdesign-component/lib/src/components/swiper/td_page_transform.dart b/tdesign-component/lib/src/components/swiper/td_page_transform.dart new file mode 100644 index 000000000..82e7633de --- /dev/null +++ b/tdesign-component/lib/src/components/swiper/td_page_transform.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_swiper_null_safety/src/transformer_page_view/transformer_page_view.dart'; + +/// TD默认PageTransformer +class TDPageTransformer extends PageTransformer { + + /// 缩放比例 + final double? scale; + /// 淡化比例 + final double? fade; + /// 左右间隔 + final double? margin; + + /// 普通margin的卡片式 + TDPageTransformer.margin({this.margin = 6.0}) + : fade = 1, + scale = 1; + + /// 缩放或透明的卡片式 + TDPageTransformer.scaleAndFade({this.fade = 1, this.scale = 0.8}) + : margin = 0.0; + + TDPageTransformer({this.fade, this.scale, this.margin}); + + @override + Widget transform(Widget item, TransformInfo info) { + var position = info.position; + var child = item; + if (scale != null) { + var scaleFactor = (1 - position.abs()) * (1 - scale!); + var rawScale = scale! + scaleFactor; + + child = Transform.scale( + scale: rawScale, + child: item, + ); + } + + if (fade != null) { + var fadeFactor = (1 - position.abs()) * (1 - fade!); + var opacity = fade! + fadeFactor; + child = Opacity( + opacity: opacity, + child: child, + ); + } + if (margin != null) { + child = Container( + margin: EdgeInsets.only(left: margin!, right: margin!), + child: child, + ); + } + return child; + } +} diff --git a/lib/src/components/swiper/td_swiper.dart b/tdesign-component/lib/src/components/swiper/td_swiper.dart similarity index 77% rename from lib/src/components/swiper/td_swiper.dart rename to tdesign-component/lib/src/components/swiper/td_swiper.dart index 4bbda591b..ea2cb3bdf 100644 --- a/lib/src/components/swiper/td_swiper.dart +++ b/tdesign-component/lib/src/components/swiper/td_swiper.dart @@ -6,27 +6,26 @@ import '../../theme/td_theme.dart'; const _kAminatedDuration = 100; +/// TDesign风格的Swiper指示器样式,与flutter_swiper的Swiper结合使用 class TDSwiperPagination extends SwiperPlugin { - - const TDSwiperPagination( {this.alignment, - this.key, - this.margin = const EdgeInsets.all(10.0), - this.builder = TDSwiperPagination.dots}); + this.key, + this.margin = const EdgeInsets.all(10.0), + this.builder = TDSwiperPagination.dots}); /// 圆点样式 static const SwiperPlugin dots = TDSwiperDotsPagination(); /// 圆角矩形 + 圆点样式 默认宽度20,高度6 - static const SwiperPlugin roundedRectangle = + static const SwiperPlugin dotsBar = TDSwiperDotsPagination(roundedRectangleWidth: 20); /// 数字样式 static const SwiperPlugin fraction = TDFractionPagination(); /// 箭头样式 - static const SwiperPlugin arrow = TDSwiperArrowPagination(); + static const SwiperPlugin controls = TDSwiperArrowPagination(); /// 当 scrollDirection== Axis.horizontal 时,默认Alignment.bottomCenter /// 当 scrollDirection== Axis.vertical 时,默认Alignment.centerRight @@ -40,7 +39,6 @@ class TDSwiperPagination extends SwiperPlugin { final Key? key; - @override Widget build(BuildContext context, SwiperPluginConfig config) { var alignment = this.alignment ?? @@ -87,12 +85,12 @@ class TDSwiperDotsPagination extends SwiperPlugin { final Key? key; const TDSwiperDotsPagination({ - this.activeColor = Colors.white, - this.color = const Color(0x8CFFFFFF), + this.activeColor, + this.color, this.key, this.size = 6.0, this.activeSize = 6.0, - this.space = 8.0, + this.space = 4.0, this.roundedRectangleWidth, this.animationDuration, }); @@ -103,14 +101,14 @@ class TDSwiperDotsPagination extends SwiperPlugin { print('warning: The itemCount is too big, ' 'we suggest use TDFractionPaginationBuilder'); } - var activeColor = this.activeColor; - var color = this.color; - - if (activeColor == null || color == null) { - var themeData = Theme.of(context); - activeColor = this.activeColor ?? themeData.primaryColor; - color = this.color ?? themeData.scaffoldBackgroundColor; - } + var activeColor = this.activeColor ?? + (config.outer + ? TDTheme.of(context).brandNormalColor + : TDTheme.of(context).whiteColor1); + var color = this.color ?? + (config.outer + ? TDTheme.of(context).grayColor3 + : TDTheme.of(context).whiteColor1.withOpacity(0.55)); if (config.indicatorLayout != PageIndicatorLayout.NONE && config.layout == SwiperLayout.DEFAULT) { @@ -134,6 +132,13 @@ class TDSwiperDotsPagination extends SwiperPlugin { var active = i == activeIndex; var isActiviRectangle = roundedRectangleWidth != null && roundedRectangleWidth! > 0 && active; + double? scalableLen; + double? fixedLen; + + scalableLen = isActiviRectangle + ? roundedRectangleWidth + : (active ? activeSize : size); + fixedLen = active ? activeSize : size; list.add(Container( key: Key('pagination_$i'), @@ -141,10 +146,8 @@ class TDSwiperDotsPagination extends SwiperPlugin { child: AnimatedContainer( duration: Duration(milliseconds: animationDuration ?? _kAminatedDuration), - width: isActiviRectangle - ? roundedRectangleWidth - : (active ? activeSize : size), - height: active ? activeSize : size, + width: config.scrollDirection == Axis.horizontal ? scalableLen : fixedLen, + height: config.scrollDirection == Axis.horizontal ? fixedLen : scalableLen, decoration: BoxDecoration( color: active ? activeColor : color, borderRadius: BorderRadius.circular(activeSize / 2)), @@ -287,30 +290,42 @@ class TDSwiperArrowPagination extends SwiperPlugin { Visibility( visible: config.loop || ((autoHideWhenAtBoundary ?? false) && activeIndex != 0), - child: CircleAvatar( - radius: radius ?? 10.0, - backgroundColor: backgroundColor ?? TDTheme.of(context).fontGyColor3, - child: backArrow ?? - const Icon( - Icons.arrow_back_ios_outlined, - color: Colors.white, - size: 9, - ), + child: GestureDetector( + child: CircleAvatar( + radius: radius ?? 10.0, + backgroundColor: + backgroundColor ?? TDTheme.of(context).fontGyColor3, + child: backArrow ?? + const Icon( + Icons.arrow_back_ios_outlined, + color: Colors.white, + size: 9, + ), + ), + onTap: () { + config.controller.previous(); + }, ), ), const Spacer(), Visibility( visible: config.loop || ((autoHideWhenAtBoundary ?? false) && activeIndex != itemCount - 1), - child: CircleAvatar( - radius: radius ?? 10.0, - backgroundColor: backgroundColor ?? TDTheme.of(context).fontGyColor3, - child: forwardArrow ?? - const Icon( - Icons.arrow_forward_ios_outlined, - color: Colors.white, - size: 9, - ), + child: GestureDetector( + child: CircleAvatar( + radius: radius ?? 10.0, + backgroundColor: + backgroundColor ?? TDTheme.of(context).fontGyColor3, + child: forwardArrow ?? + const Icon( + Icons.arrow_forward_ios_outlined, + color: Colors.white, + size: 9, + ), + ), + onTap: () { + config.controller.next(); + }, ), ), ]); diff --git a/tdesign-component/lib/src/components/switch/td_cupertino_switch.dart b/tdesign-component/lib/src/components/switch/td_cupertino_switch.dart new file mode 100644 index 000000000..f2753a5db --- /dev/null +++ b/tdesign-component/lib/src/components/switch/td_cupertino_switch.dart @@ -0,0 +1,644 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui' show lerpDouble; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; + +// Examples can assume: +// bool _lights = false; +// void setState(VoidCallback fn) { } + +/// An iOS-style switch. +/// +/// Used to toggle the on/off state of a single setting. +/// +/// The switch itself does not maintain any state. Instead, when the state of +/// the switch changes, the widget calls the [onChanged] callback. Most widgets +/// that use a switch will listen for the [onChanged] callback and rebuild the +/// switch with a new [value] to update the visual appearance of the switch. +/// +/// {@tool dartpad} +/// This example shows a toggleable [TDCupertinoSwitch]. When the thumb slides to +/// the other side of the track, the switch is toggled between on/off. +/// +/// ** See code in examples/api/lib/cupertino/switch/cupertino_switch.0.dart ** +/// {@end-tool} +/// +/// {@tool snippet} +/// +/// This sample shows how to use a [TDCupertinoSwitch] in a [ListTile]. The +/// [MergeSemantics] is used to turn the entire [ListTile] into a single item +/// for accessibility tools. +/// +/// ```dart +/// MergeSemantics( +/// child: ListTile( +/// title: const Text('Lights'), +/// trailing: TDCupertinoSwitch( +/// value: _lights, +/// onChanged: (bool value) { setState(() { _lights = value; }); }, +/// ), +/// onTap: () { setState(() { _lights = !_lights; }); }, +/// ), +/// ) +/// ``` +/// {@end-tool} +/// +/// See also: +/// +/// * [Switch], the Material Design equivalent. +/// * +class TDCupertinoSwitch extends StatefulWidget { + /// Creates an iOS-style switch. + /// + /// The [value] parameter must not be null. + /// The [dragStartBehavior] parameter defaults to [DragStartBehavior.start] and must not be null. + const TDCupertinoSwitch({ + Key? key, + required this.value, + required this.onChanged, + this.activeColor, + this.trackColor, + this.thumbColor, + this.thumbView, + this.dragStartBehavior = DragStartBehavior.start, + }) : super(key: key); + + /// Whether this switch is on or off. + /// + /// Must not be null. + final bool value; + + /// Called when the user toggles with switch on or off. + /// + /// The switch passes the new value to the callback but does not actually + /// change state until the parent widget rebuilds the switch with the new + /// value. + /// + /// If null, the switch will be displayed as disabled, which has a reduced opacity. + /// + /// The callback provided to onChanged should update the state of the parent + /// [StatefulWidget] using the [State.setState] method, so that the parent + /// gets rebuilt; for example: + /// + /// ```dart + /// TDCupertinoSwitch( + /// value: _giveVerse, + /// onChanged: (bool newValue) { + /// setState(() { + /// _giveVerse = newValue; + /// }); + /// }, + /// ) + /// ``` + final ValueChanged? onChanged; + + /// The color to use when this switch is on. + /// + /// Defaults to [CupertinoColors.systemGreen] when null and ignores + /// the [CupertinoTheme] in accordance to native iOS behavior. + final Color? activeColor; + + /// The color to use for the background when the switch is off. + /// + /// Defaults to [CupertinoColors.secondarySystemFill] when null. + final Color? trackColor; + + /// The color to use for the thumb of the switch. + /// + /// Defaults to [CupertinoColors.white] when null. + final Color? thumbColor; + + /// The custom widget over the thumb. + final Widget? thumbView; + + /// {@template flutter.cupertino.TDCupertinoSwitch.dragStartBehavior} + /// Determines the way that drag start behavior is handled. + /// + /// If set to [DragStartBehavior.start], the drag behavior used to move the + /// switch from on to off will begin at the position where the drag gesture won + /// the arena. If set to [DragStartBehavior.down] it will begin at the position + /// where a down event was first detected. + /// + /// In general, setting this to [DragStartBehavior.start] will make drag + /// animation smoother and setting it to [DragStartBehavior.down] will make + /// drag behavior feel slightly more reactive. + /// + /// By default, the drag start behavior is [DragStartBehavior.start]. + /// + /// See also: + /// + /// * [DragGestureRecognizer.dragStartBehavior], which gives an example for + /// the different behaviors. + /// + /// {@endtemplate} + final DragStartBehavior dragStartBehavior; + + @override + State createState() => _TDCupertinoSwitchState(); + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties.add(FlagProperty('value', + value: value, ifTrue: 'on', ifFalse: 'off', showName: true)); + properties.add(ObjectFlagProperty>( + 'onChanged', onChanged, + ifNull: 'disabled')); + } +} + +class _TDCupertinoSwitchState extends State + with TickerProviderStateMixin { + late TapGestureRecognizer _tap; + late HorizontalDragGestureRecognizer _drag; + + late AnimationController _positionController; + late CurvedAnimation position; + + late AnimationController _reactionController; + late Animation _reaction; + + bool get isInteractive => widget.onChanged != null; + + // A non-null boolean value that changes to true at the end of a drag if the + // switch must be animated to the position indicated by the widget's value. + bool needsPositionAnimation = false; + + @override + void initState() { + super.initState(); + + _tap = TapGestureRecognizer() + ..onTapDown = _handleTapDown + ..onTapUp = _handleTapUp + ..onTap = _handleTap + ..onTapCancel = _handleTapCancel; + _drag = HorizontalDragGestureRecognizer() + ..onStart = _handleDragStart + ..onUpdate = _handleDragUpdate + ..onEnd = _handleDragEnd + ..dragStartBehavior = widget.dragStartBehavior; + + _positionController = AnimationController( + duration: _kToggleDuration, + value: widget.value ? 1.0 : 0.0, + vsync: this, + ); + position = CurvedAnimation( + parent: _positionController, + curve: Curves.linear, + ); + _reactionController = AnimationController( + duration: _kReactionDuration, + vsync: this, + ); + _reaction = CurvedAnimation( + parent: _reactionController, + curve: Curves.ease, + ); + } + + @override + void didUpdateWidget(TDCupertinoSwitch oldWidget) { + super.didUpdateWidget(oldWidget); + _drag.dragStartBehavior = widget.dragStartBehavior; + + if (needsPositionAnimation || oldWidget.value != widget.value) { + _resumePositionAnimation(isLinear: needsPositionAnimation); + } + } + + // `isLinear` must be true if the position animation is trying to move the + // thumb to the closest end after the most recent drag animation, so the curve + // does not change when the controller's value is not 0 or 1. + // + // It can be set to false when it's an implicit animation triggered by + // widget.value changes. + void _resumePositionAnimation({bool isLinear = true}) { + needsPositionAnimation = false; + position + ..curve = isLinear ? Curves.linear : Curves.ease + ..reverseCurve = isLinear ? Curves.linear : Curves.ease.flipped; + if (widget.value) { + _positionController.forward(); + } else { + _positionController.reverse(); + } + } + + void _handleTapDown(TapDownDetails details) { + if (isInteractive) { + needsPositionAnimation = false; + } + _reactionController.forward(); + } + + void _handleTap() { + if (isInteractive) { + widget.onChanged!(!widget.value); + _emitVibration(); + } + } + + void _handleTapUp(TapUpDetails details) { + if (isInteractive) { + needsPositionAnimation = false; + _reactionController.reverse(); + } + } + + void _handleTapCancel() { + if (isInteractive) { + _reactionController.reverse(); + } + } + + void _handleDragStart(DragStartDetails details) { + if (isInteractive) { + needsPositionAnimation = false; + _reactionController.forward(); + _emitVibration(); + } + } + + void _handleDragUpdate(DragUpdateDetails details) { + if (isInteractive) { + position + ..curve = Curves.linear + ..reverseCurve = Curves.linear; + final delta = details.primaryDelta! / _kTrackInnerLength; + switch (Directionality.of(context)) { + case TextDirection.rtl: + _positionController.value -= delta; + break; + case TextDirection.ltr: + _positionController.value += delta; + break; + } + } + } + + void _handleDragEnd(DragEndDetails details) { + // Deferring the animation to the next build phase. + setState(() { + needsPositionAnimation = true; + }); + // Call onChanged when the user's intent to change value is clear. + if (position.value >= 0.5 != widget.value) { + widget.onChanged!(!widget.value); + } + _reactionController.reverse(); + } + + void _emitVibration() { + switch (defaultTargetPlatform) { + case TargetPlatform.iOS: + HapticFeedback.lightImpact(); + break; + default: + break; + } + } + + @override + Widget build(BuildContext context) { + if (needsPositionAnimation) { + _resumePositionAnimation(); + } + return MouseRegion( + cursor: isInteractive && kIsWeb + ? SystemMouseCursors.click + : MouseCursor.defer, + child: Opacity( + opacity: + widget.onChanged == null ? _kTDCupertinoSwitchDisabledOpacity : 1.0, + child: _TDCupertinoSwitchRenderObjectWidget( + value: widget.value, + activeColor: CupertinoDynamicColor.resolve( + widget.activeColor ?? CupertinoColors.systemGreen, + context, + ), + trackColor: CupertinoDynamicColor.resolve( + widget.trackColor ?? CupertinoColors.secondarySystemFill, + context), + thumbColor: CupertinoDynamicColor.resolve( + widget.thumbColor ?? CupertinoColors.white, context), + onChanged: widget.onChanged, + textDirection: Directionality.of(context), + state: this, + child: widget.thumbView, + ), + ), + ); + } + + @override + void dispose() { + _tap.dispose(); + _drag.dispose(); + + _positionController.dispose(); + _reactionController.dispose(); + super.dispose(); + } +} + +class _TDCupertinoSwitchRenderObjectWidget + extends SingleChildRenderObjectWidget { + const _TDCupertinoSwitchRenderObjectWidget({ + required this.child, + required this.value, + required this.activeColor, + required this.trackColor, + required this.thumbColor, + required this.onChanged, + required this.textDirection, + required this.state, + }); + + final bool value; + final Color activeColor; + final Color trackColor; + final Color thumbColor; + final ValueChanged? onChanged; + final _TDCupertinoSwitchState state; + final TextDirection textDirection; + + /// The widget below this widget in the tree. + /// + /// {@macro flutter.widgets.ProxyWidget.child} + @override + final Widget? child; + + @override + _RenderTDCupertinoSwitch createRenderObject(BuildContext context) { + return _RenderTDCupertinoSwitch( + value: value, + activeColor: activeColor, + trackColor: trackColor, + thumbColor: thumbColor, + onChanged: onChanged, + textDirection: textDirection, + state: state, + ); + } + + @override + void updateRenderObject( + BuildContext context, _RenderTDCupertinoSwitch renderObject) { + renderObject + ..value = value + ..activeColor = activeColor + ..trackColor = trackColor + ..thumbColor = thumbColor + ..onChanged = onChanged + ..textDirection = textDirection; + } +} + +const double _kTrackWidth = 45.0; +const double _kTrackHeight = 28.0; +const double _kTrackRadius = _kTrackHeight / 2.0; +const double _kTrackInnerStart = _kTrackHeight / 2.0; +const double _kTrackInnerEnd = _kTrackWidth - _kTrackInnerStart; +const double _kTrackInnerLength = _kTrackInnerEnd - _kTrackInnerStart; +const double _kSwitchWidth = 45.0; +const double _kSwitchHeight = 28.0; +// Opacity of a disabled switch, as eye-balled from iOS Simulator on Mac. +const double _kTDCupertinoSwitchDisabledOpacity = 0.5; +// In the normal switch case, the closed thumb is smaller than the open thumb. +// Same size as if there is a child widget on thumb. +const double _kSwitchOnThumbRadius = 11; +const double _kSwitchOffThumbRadius = 8; +const double _kSwitchOnThumbMargin = (_kTrackHeight - _kSwitchOnThumbRadius * 2) / 2; +const double _kSwitchOffThumbMargin = (_kTrackHeight - _kSwitchOffThumbRadius * 2) / 2; + +const Duration _kReactionDuration = Duration(milliseconds: 300); +const Duration _kToggleDuration = Duration(milliseconds: 200); + +class _RenderTDCupertinoSwitch extends RenderConstrainedBox { + _RenderTDCupertinoSwitch({ + required bool value, + required Color activeColor, + required Color trackColor, + required Color thumbColor, + ValueChanged? onChanged, + required TextDirection textDirection, + required _TDCupertinoSwitchState state, + }) : _value = value, + _activeColor = activeColor, + _trackColor = trackColor, + _thumbPainter = CupertinoThumbPainter.switchThumb(color: thumbColor), + _onChanged = onChanged, + _textDirection = textDirection, + _state = state, + super( + additionalConstraints: const BoxConstraints.tightFor( + width: _kSwitchWidth, height: _kSwitchHeight)) { + state.position.addListener(markNeedsPaint); + state._reaction.addListener(markNeedsPaint); + } + + final _TDCupertinoSwitchState _state; + + bool get value => _value; + bool _value; + + set value(bool value) { + if (value == _value) { + return; + } + _value = value; + markNeedsSemanticsUpdate(); + } + + Color get activeColor => _activeColor; + Color _activeColor; + + set activeColor(Color value) { + if (value == _activeColor) { + return; + } + _activeColor = value; + markNeedsPaint(); + } + + Color get trackColor => _trackColor; + Color _trackColor; + + set trackColor(Color value) { + if (value == _trackColor) { + return; + } + _trackColor = value; + markNeedsPaint(); + } + + Color get thumbColor => _thumbPainter.color; + CupertinoThumbPainter _thumbPainter; + + set thumbColor(Color value) { + if (value == thumbColor) { + return; + } + _thumbPainter = CupertinoThumbPainter.switchThumb(color: value); + markNeedsPaint(); + } + + ValueChanged? get onChanged => _onChanged; + ValueChanged? _onChanged; + + set onChanged(ValueChanged? value) { + if (value == _onChanged) { + return; + } + final wasInteractive = isInteractive; + _onChanged = value; + if (wasInteractive != isInteractive) { + markNeedsPaint(); + markNeedsSemanticsUpdate(); + } + } + + TextDirection get textDirection => _textDirection; + TextDirection _textDirection; + + set textDirection(TextDirection value) { + if (_textDirection == value) { + return; + } + _textDirection = value; + markNeedsPaint(); + } + + bool get isInteractive => onChanged != null; + + double get thumbRadius { + if (child == null) { + final value = _state.position.value; + return (_kSwitchOnThumbRadius - _kSwitchOffThumbRadius) * value + _kSwitchOffThumbRadius; + } + return _kSwitchOnThumbRadius; + } + + double get thumbMargin { + if (child == null) { + return value ? _kSwitchOffThumbMargin : _kSwitchOffThumbMargin; + } + return _kSwitchOnThumbMargin; + } + + @override + bool hitTestSelf(Offset position) => true; + + @override + void handleEvent(PointerEvent event, BoxHitTestEntry entry) { + assert(debugHandleEvent(event, entry)); + if (event is PointerDownEvent && isInteractive) { + _state._drag.addPointer(event); + _state._tap.addPointer(event); + } + } + + @override + void describeSemanticsConfiguration(SemanticsConfiguration config) { + super.describeSemanticsConfiguration(config); + + if (isInteractive) { + config.onTap = _state._handleTap; + } + + config.isEnabled = isInteractive; + config.isToggled = _value; + } + + @override + void paint(PaintingContext context, Offset offset) { + final canvas = context.canvas; + + final currentValue = _state.position.value; + final currentReactionValue = _state._reaction.value; + + final double visualPosition; + switch (textDirection) { + case TextDirection.rtl: + visualPosition = 1.0 - currentValue; + break; + case TextDirection.ltr: + visualPosition = currentValue; + break; + } + + final paint = Paint() + ..color = Color.lerp(trackColor, activeColor, currentValue)!; + + final trackRect = Rect.fromLTWH( + offset.dx + (size.width - _kTrackWidth) / 2.0, + offset.dy + (size.height - _kTrackHeight) / 2.0, + _kTrackWidth, + _kTrackHeight, + ); + final trackRRect = RRect.fromRectAndRadius( + trackRect, const Radius.circular(_kTrackRadius)); + canvas.drawRRect(trackRRect, paint); + + final currentThumbExtension = + CupertinoThumbPainter.extension * currentReactionValue; + final thumbLeft = lerpDouble( + trackRect.left + _kTrackInnerStart - thumbRadius, + trackRect.left + _kTrackInnerEnd - thumbRadius - currentThumbExtension, + visualPosition, + )!; + final thumbRight = lerpDouble( + trackRect.left + _kTrackInnerStart + thumbRadius + currentThumbExtension, + trackRect.left + _kTrackInnerEnd + thumbRadius, + visualPosition, + )!; + final thumbCenterY = offset.dy + size.height / 2.0; + final thumbBounds = Rect.fromLTRB( + thumbLeft, + thumbCenterY - thumbRadius, + thumbRight, + thumbCenterY + thumbRadius, + ); + + _clipRRectLayer.layer = context + .pushClipRRect(needsCompositing, Offset.zero, thumbBounds, trackRRect, + (PaintingContext innerContext, Offset offset) { + _thumbPainter.paint(innerContext.canvas, thumbBounds); + }, oldLayer: _clipRRectLayer.layer); + if (child != null) { + context.paintChild(child!, Offset(thumbBounds.left + thumbMargin, 0)); + } + } + + final LayerHandle _clipRRectLayer = + LayerHandle(); + + @override + void dispose() { + _clipRRectLayer.layer = null; + super.dispose(); + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder description) { + super.debugFillProperties(description); + description.add(FlagProperty('value', + value: value, ifTrue: 'checked', ifFalse: 'unchecked', showName: true)); + description.add(FlagProperty('isInteractive', + value: isInteractive, + ifTrue: 'enabled', + ifFalse: 'disabled', + showName: true, + defaultValue: true)); + } +} diff --git a/tdesign-component/lib/src/components/switch/td_loading_paint.dart b/tdesign-component/lib/src/components/switch/td_loading_paint.dart new file mode 100644 index 000000000..98c45e107 --- /dev/null +++ b/tdesign-component/lib/src/components/switch/td_loading_paint.dart @@ -0,0 +1,43 @@ +import 'dart:math'; +import 'dart:ui' as ui; + +import 'package:flutter/cupertino.dart'; + +class TDLoadingPainter extends CustomPainter { + final Color color; + final double width; + + TDLoadingPainter({required this.color, required this.width}); + + final _paint = Paint()..style = PaintingStyle.stroke; + + @override + void paint(Canvas canvas, Size size) { + var minLength = min(size.width, size.height); + _paint.strokeWidth = width; + _paint.shader = ui.Gradient.sweep(Offset(size.width / 2, size.height / 2), + [const Color(0x01ffffff), color]); + if (minLength == size.width) { + canvas.drawArc( + Rect.fromLTWH( + 0, (size.height - size.width) / 2, size.width, size.width), + 0, + pi * 2, + false, + _paint); + } else { + canvas.drawArc( + Rect.fromLTWH( + (size.width - size.height) / 2, 0, size.height, size.height), + 0, + pi * 2, + false, + _paint); + } + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) { + return true; + } +} diff --git a/tdesign-component/lib/src/components/switch/td_switch.dart b/tdesign-component/lib/src/components/switch/td_switch.dart new file mode 100644 index 000000000..9bdac3b9f --- /dev/null +++ b/tdesign-component/lib/src/components/switch/td_switch.dart @@ -0,0 +1,206 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; +import 'td_cupertino_switch.dart'; + +/// 开关改变事件处理 +typedef OnSwitchChanged = bool Function(bool value); + +enum TDSwitchSize { large, medium, small } + +enum TDSwitchType { fill, text, loading, icon } + +class TDSwitch extends StatefulWidget { + const TDSwitch({ + Key? key, + this.enable = true, + this.isOn = false, + this.size = TDSwitchSize.medium, + this.type = TDSwitchType.fill, + this.trackOnColor, + this.trackOffColor, + this.thumbContentOnColor, + this.thumbContentOffColor, + this.thumbContentOnFont, + this.thumbContentOffFont, + this.onChanged, + this.openText, + this.closeText, + }) : super(key: key); + + /// 是否可点击 + final bool enable; + + /// 是否打开 + final bool isOn; + + /// 开启时轨道颜色 + final Color? trackOnColor; + + /// 关闭时轨道颜色 + final Color? trackOffColor; + + /// 开启时ThumbView的颜色 + final Color? thumbContentOnColor; + + /// 关闭时ThumbView的颜色 + final Color? thumbContentOffColor; + + /// 开启时ThumbView的字体样式 + final TextStyle? thumbContentOnFont; + + /// 关闭时ThumbView的字体样式 + final TextStyle? thumbContentOffFont; + + /// 尺寸:大、中、小 + final TDSwitchSize? size; + + /// 类型:填充、文本、加载 + final TDSwitchType? type; + + /// 改变事件 + final OnSwitchChanged? onChanged; + + /// 打开文案 + final String? openText; + + /// 关闭文案 + final String? closeText; + + @override + State createState() { + return TDSwitchState(); + } +} + +class TDSwitchState extends State { + bool isOn = false; + + @override + void initState() { + super.initState(); + isOn = widget.isOn; + } + + @override + void didUpdateWidget(covariant TDSwitch oldWidget) { + super.didUpdateWidget(oldWidget); + isOn = widget.isOn; + } + + @override + Widget build(BuildContext context) { + final theme = TDTheme.of(context); + final switchEnable = widget.enable && widget.type != TDSwitchType.loading; + final trackOnColor = widget.trackOnColor ?? theme.brandColor7; + final trackOffColor = widget.trackOffColor ?? theme.grayColor4; + final thumbContentOnColor = + widget.thumbContentOnColor ?? theme.brandNormalColor; + final thumbContentOffColor = + widget.thumbContentOffColor ?? theme.fontGyColor4; + final thumbContentOnFont = widget.thumbContentOnFont ?? const TextStyle(fontSize: 14); + final thumbContentOffFont = widget.thumbContentOffFont ?? const TextStyle(fontSize: 14); + Widget current = TDCupertinoSwitch( + value: isOn, + activeColor: trackOnColor, + trackColor: trackOffColor, + onChanged: (value) { + var process = widget.onChanged?.call(value) ?? false; + // 如果外部未处理,才需要自定刷新开关,如果已处理则不需要刷新 + if (!process) { + isOn = value; + setState(() {}); + } + }, + thumbView: _getThumbView(thumbContentOnColor, thumbContentOffColor,thumbContentOnFont, thumbContentOffFont), + ); + if (!switchEnable) { + current = Opacity( + opacity: 0.4, + child: IgnorePointer( + ignoring: !switchEnable, + child: current, + ), + ); + } + return SizedBox( + width: _getWidth(), + height: _getHeight(), + child: FittedBox( + child: current, + ), + ); + // return ConstrainedBox( _getWidth(), height: _getHeight(), child: current); + } + + double _getWidth() { + switch (widget.size) { + case TDSwitchSize.large: + return 52; + case TDSwitchSize.medium: + return 45; + case TDSwitchSize.small: + return 39; + default: + return 45; + } + } + + double _getHeight() { + switch (widget.size) { + case TDSwitchSize.large: + return 32; + case TDSwitchSize.medium: + return 28; + case TDSwitchSize.small: + return 24; + default: + return 28; + } + } + + Widget? _getThumbView(Color thumbContentOnColor, Color thumbContentOffColor, TextStyle thumbContentOnFont, TextStyle thumbContentOffFont) { + switch (widget.type) { + case TDSwitchType.text: + return Stack( + children: [ + Container( + alignment: Alignment.center, + width: 16, + child: TDText( + isOn + ? (widget.openText ?? context.resource.open) + : (widget.closeText ?? context.resource.close), + textColor: isOn ? thumbContentOnColor : thumbContentOffColor, + forceVerticalCenter: true, + maxLines: 1, + style: isOn ? thumbContentOnFont : thumbContentOffFont, + ), + ) + ], + ); + case TDSwitchType.loading: + return Container( + alignment: Alignment.centerLeft, + child: TDCircleIndicator( + color: thumbContentOnColor, + size: 16, + lineWidth: 3, + ), + ); + case TDSwitchType.icon: + return Container( + alignment: Alignment.centerLeft, + child: Icon(isOn ? TDIcons.check : TDIcons.close, + size: 16, + color: isOn ? thumbContentOnColor : thumbContentOffColor), + ); + case TDSwitchType.fill: + default: + return null; + } + } +} diff --git a/tdesign-component/lib/src/components/tabbar/td_bottom_tab_bar.dart b/tdesign-component/lib/src/components/tabbar/td_bottom_tab_bar.dart new file mode 100644 index 000000000..5253e0b59 --- /dev/null +++ b/tdesign-component/lib/src/components/tabbar/td_bottom_tab_bar.dart @@ -0,0 +1,932 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +/// 展开项 向下箭头宽 +const double _kArrowWidth = 13.5; + +/// 展开项 向下箭头高 +const double _kArrowHeight = 8; + +/// 展开项选项弹窗 单个item最低高度 +const double _kMenuItemMinHeight = 23; + +/// 展开项弹窗 单个item默认高度 +const double _kDefaultMenuItemHeight = 48; + +/// 展开项弹窗 单个item默认宽度为按钮宽度-20 +const double _kDefaultMenuItemWidthShrink = 20; + +/// 导航栏默认高度 +const double _kDefaultTabBarHeight = 56; + +/// 展开项弹窗弹出动画时间 +const Duration _kPopupMenuDuration = Duration(milliseconds: 10); + +enum TDBottomTabBarBasicType { + /// 单层级纯文本标签栏 + text, + + /// 文本加图标标签栏 + iconText, + + /// 纯图标标签栏 + icon, + + /// 双层级纯文本标签栏 + expansionPanel, +} + +enum TDBottomTabBarComponentType { + /// 普通样式 + normal, + + /// 带胶囊背景的item选中样式 + label +} + +enum TDBottomTabBarOutlineType { + /// 填充样式 + filled, + + /// 胶囊样式 + capsule +} + +/// 飘新配置 +class BadgeConfig { + BadgeConfig({ + required this.showBadge, + TDBadge? tdBadge, + this.badgeTopOffset, + this.badgeRightOffset, + }) : tdBadge = tdBadge ?? const TDBadge(TDBadgeType.redPoint); + + /// 是否展示消息 + final bool showBadge; + + /// 消息样式(未设置但showBadge为true,则默认使用红点) + final TDBadge? tdBadge; + + /// 消息顶部偏移量 + final double? badgeTopOffset; + + /// 消息右侧偏移量 + final double? badgeRightOffset; +} + +/// 单个tab配置 +class TDBottomTabBarTabConfig { + TDBottomTabBarTabConfig({ + required this.onTap, + this.selectedIcon, + this.unselectedIcon, + this.tabText, + this.selectTabTextStyle, + this.unselectTabTextStyle, + this.badgeConfig, + this.popUpButtonConfig, + this.onLongPress, + this.allowMultipleTaps = false + }) : assert(() { + if (badgeConfig?.showBadge ?? false) { + if (badgeConfig?.tdBadge == null) { + throw FlutterError('[NavigationTab] if set showBadge = true, ' + 'you must set a tdBadge instance'); + } + } + return true; + }()); + + /// 选中时图标 + final Widget? selectedIcon; + + /// 未选中时图标 + final Widget? unselectedIcon; + + /// tab文本 + final String? tabText; + + /// 文本已选择样式 basicType为text时必填 + final TextStyle? selectTabTextStyle; + + /// 文本未选择样式 basicType为text时必填 + final TextStyle? unselectTabTextStyle; + + /// tab点击事件 + final GestureTapCallback? onTap; + + /// 消息配置 + final BadgeConfig? badgeConfig; + + /// 弹窗配置 + final TDBottomTabBarPopUpBtnConfig? popUpButtonConfig; + + /// onTap方法允许点击多次 + final bool allowMultipleTaps; + + /// 长按事件 + final GestureLongPressCallback? onLongPress; +} + +class TDBottomTabBar extends StatefulWidget { + TDBottomTabBar( + this.basicType, { + Key? key, + this.componentType = TDBottomTabBarComponentType.label, + this.outlineType = TDBottomTabBarOutlineType.filled, + required this.navigationTabs, + this.barHeight = _kDefaultTabBarHeight, + this.useVerticalDivider, + this.dividerHeight, + this.dividerThickness, + this.dividerColor, + this.showTopBorder = true, + this.topBorder, + this.useSafeArea = true, + this.selectedBgColor, + this.unselectedBgColor, + this.backgroundColor, + this.centerDistance, + this.currentIndex, + this.needInkWell = false, + }) : assert(() { + if (navigationTabs.isEmpty) { + throw FlutterError('[TDBottomTabBar] please set at least one tab!'); + } + if (basicType == TDBottomTabBarBasicType.text) { + for (final item in navigationTabs) { + if (item.tabText == null) { + throw FlutterError( + '[TDBottomTabBar] type is TDBottomBarType.text, but not set tabText.'); + } + } + } + if (basicType == TDBottomTabBarBasicType.icon) { + for (final item in navigationTabs) { + if (item.selectedIcon == null || item.unselectedIcon == null) { + throw FlutterError( + '[TDBottomTabBar] type is TDBottomBarType.icon,' + 'but has no set icon.'); + } + } + } + if (basicType == TDBottomTabBarBasicType.iconText) { + for (final item in navigationTabs) { + if (item.tabText == null || + item.selectedIcon == null || + item.unselectedIcon == null) { + throw FlutterError( + '[TDBottomTabBar] type is TDBottomBarType.iconText,' + 'but not set tabText or icon.'); + } + } + } + if (currentIndex != null && + (currentIndex < 0 || currentIndex >= navigationTabs.length)) { + throw FlutterError( + '[TDBottomTabBar] currentIndex must in [0,navigationTabs.length)'); + } + return true; + }()), + super(key: key); + + /// 基本样式(纯文本、纯图标、图标+文本) + final TDBottomTabBarBasicType basicType; + + /// 选项样式 默认label + final TDBottomTabBarComponentType? componentType; + + /// 标签栏样式 默认filled + final TDBottomTabBarOutlineType? outlineType; + + /// tabs配置 + final List navigationTabs; + + /// tab高度 + final double? barHeight; + + /// 是否使用竖线分隔(如果选项样式为label则强制为false) + final bool? useVerticalDivider; + + /// 分割线高度(可选) + final double? dividerHeight; + + /// 分割线厚度(可选) + final double? dividerThickness; + + /// 分割线颜色(可选) + final Color? dividerColor; + + /// 是否展示bar上边线(设置为true 但是topBorder样式未设置,则使用默认值,非胶囊型才生效) + final bool? showTopBorder; + + /// 上边线样式 + final BorderSide? topBorder; + + /// 使用安全区域 + final bool useSafeArea; + + /// 选中时背景颜色 + final Color? selectedBgColor; + + /// 未选中时背景颜色 + final Color? unselectedBgColor; + + /// 背景颜色 (可选) + final Color? backgroundColor; + + /// icon与文本中间距离(可选) + final double? centerDistance; + + /// 选中的index(可选) + final int? currentIndex; + + /// 是否需要水波纹效果 + final bool needInkWell; + + @override + State createState() => _TDBottomTabBarState(); +} + +class _TDBottomTabBarState extends State { + int _selectedIndex = 0; + + @override + void initState() { + super.initState(); + _selectedIndex = widget.currentIndex ?? 0; + } + + @override + void didUpdateWidget(covariant TDBottomTabBar oldWidget) { + super.didUpdateWidget(oldWidget); + _selectedIndex = widget.currentIndex ?? _selectedIndex; + } + + @override + Widget build(BuildContext context) { + var isCapsuleOutlineType = + widget.outlineType == TDBottomTabBarOutlineType.capsule; + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + /// -2 是为了增加边框 + var maxWidth = + double.parse(constraints.biggest.width.toStringAsFixed(1)) - 2; + + /// 胶囊样式 比正常样式宽度要小32 + if (isCapsuleOutlineType) { + maxWidth -= 32; + } + var itemWidth = maxWidth / widget.navigationTabs.length; + + Widget result = Container( + height: widget.barHeight ?? _kDefaultTabBarHeight, + alignment: Alignment.center, + margin: isCapsuleOutlineType + ? const EdgeInsets.symmetric(horizontal: 16) + : null, + decoration: BoxDecoration( + color: widget.backgroundColor ?? Colors.white, + borderRadius: + isCapsuleOutlineType ? BorderRadius.circular(56) : null, + border: widget.showTopBorder! && !isCapsuleOutlineType + ? Border( + top: widget.topBorder ?? + BorderSide( + color: TDTheme.of(context).grayColor3, + width: 0.5)) + : null, + boxShadow: isCapsuleOutlineType + ? TDTheme.of(context).shadowsTop + : null), + child: Stack(alignment: Alignment.center, children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: + List.generate(widget.navigationTabs.length, (index) { + return _item(index, itemWidth); + })), + _verticalDivider(), + ])); + if (widget.useSafeArea) { + result = SafeArea(child: result); + } + return result; + }, + ); + } + + void _onTap(int index) { + setState(() { + if (_selectedIndex != index || widget.navigationTabs[index].allowMultipleTaps) { + widget.navigationTabs[index].onTap?.call(); + } + if (_selectedIndex != index) { + _selectedIndex = index; + } + }); + } + + Widget _item(int index, double itemWidth) { + var tabItemConfig = widget.navigationTabs[index]; + return Container( + height: widget.barHeight ?? _kDefaultTabBarHeight, + width: itemWidth, + alignment: Alignment.center, + padding: EdgeInsets.only( + top: 7, + bottom: + widget.basicType == TDBottomTabBarBasicType.iconText ? 5 : 7), + child: TDBottomTabBarItemWithBadge( + basiceType: widget.basicType, + componentType: + widget.componentType ?? TDBottomTabBarComponentType.label, + outlineType: widget.outlineType ?? TDBottomTabBarOutlineType.filled, + itemConfig: tabItemConfig, + isSelected: index == _selectedIndex, + itemHeight: widget.barHeight ?? _kDefaultTabBarHeight, + itemWidth: itemWidth, + tabsLength: widget.navigationTabs.length, + selectedBgColor: widget.selectedBgColor, + unselectedBgColor: widget.unselectedBgColor, + centerDistance: widget.centerDistance ?? 0, + needInkWell: widget.needInkWell, + onTap: () { + _onTap(index); + }, + onLongPress: () { + tabItemConfig.onLongPress?.call(); + }, + )); + } + + Widget _verticalDivider() { + if (widget.componentType == TDBottomTabBarComponentType.label) {} + return Visibility( + visible: widget.componentType != TDBottomTabBarComponentType.label && + (widget.useVerticalDivider ?? false), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: List.generate(widget.navigationTabs.length - 1, (index) { + return SizedBox( + width: widget.dividerThickness ?? 0.5, + height: widget.dividerHeight ?? 32, + child: VerticalDivider( + color: widget.dividerColor ?? TDTheme.of(context).grayColor3, + thickness: widget.dividerThickness ?? 0.5, + ), + ); + }), + ), + ); + } +} + +class TDBottomTabBarItemWithBadge extends StatelessWidget { + const TDBottomTabBarItemWithBadge( + {Key? key, + required this.basiceType, + required this.componentType, + required this.outlineType, + required this.itemConfig, + required this.isSelected, + required this.itemHeight, + required this.itemWidth, + required this.onTap, + required this.tabsLength, + required this.selectedBgColor, + required this.unselectedBgColor, + required this.centerDistance, + this.onLongPress, + this.needInkWell = false}) + : super(key: key); + + /// tab基本类型 + final TDBottomTabBarBasicType basiceType; + + /// tab选中背景类型 + final TDBottomTabBarComponentType componentType; + + // + final TDBottomTabBarOutlineType outlineType; + + /// 单个tab的属性配置 + final TDBottomTabBarTabConfig itemConfig; + + /// 选中状态 + final bool isSelected; + + /// tab高度 + final double itemHeight; + + /// tab宽度 + final double itemWidth; + + /// 点击事件 + final GestureTapCallback onTap; + + /// tab总个数 + final int tabsLength; + + /// 选中时背景颜色 + final Color? selectedBgColor; + + /// 未选中时背景颜色 + final Color? unselectedBgColor; + + /// icon与文本中间距离 + final double centerDistance; + + /// 长按事件 + final GestureLongPressCallback? onLongPress; + + /// 是否需要水波纹效果 + final bool needInkWell; + + @override + Widget build(BuildContext context) { + + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: ()=>handleTap(context), + onLongPress: () { + onLongPress?.call(); + }, + child: Container( + height: itemHeight, + alignment: Alignment.center, + child: Stack( + alignment: Alignment.center, + clipBehavior: Clip.none, + children: [ + if (isSelected || unselectedBgColor != null) + Visibility( + visible: componentType == TDBottomTabBarComponentType.label, + child: Container( + /// 设计稿上 tab个数大于3时,左右边距为8,小于等于3时,左右边距为12 + width: itemWidth - (tabsLength > 3 ? 16 : 24), + height: basiceType == TDBottomTabBarBasicType.text || + basiceType == TDBottomTabBarBasicType.expansionPanel + ? 32 + : null, + decoration: BoxDecoration( + color: isSelected + ? selectedBgColor ?? TDTheme.of(context).brandColor1 + : unselectedBgColor, + borderRadius: const BorderRadius.all(Radius.circular(24)), + ), + ), + ), + _buildItem(context), + ], + ), + ), + ); + } + + Widget _badge(BadgeConfig? badgeConfig) { + if (badgeConfig?.showBadge ?? false) { + if (badgeConfig?.tdBadge != null) { + return badgeConfig!.tdBadge!; + } + } + return Container(); + } + + Widget _constructItem( + BuildContext context, BadgeConfig? badgeConfig, bool isInOrOutCapsule) { + Widget child = Container(); + if (basiceType == TDBottomTabBarBasicType.text) { + child = _textItem(context, itemConfig, isSelected, + TDTheme.of(context).fontTitleMedium!); + } + if (basiceType == TDBottomTabBarBasicType.expansionPanel) { + if (itemConfig.popUpButtonConfig != null) { + child = Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + TDIcons.view_list, + size: 16.0, + color: isSelected + ? TDTheme.of(context).brandNormalColor + : TDTheme.of(context).fontGyColor1, + ), + const SizedBox(width: 5), + _textItem(context, itemConfig, isSelected, + TDTheme.of(context).fontTitleMedium!) + ], + ); + } else { + child = _textItem(context, itemConfig, isSelected, + TDTheme.of(context).fontTitleMedium!); + } + } + if (basiceType == TDBottomTabBarBasicType.icon) { + var selectedIcon = itemConfig.selectedIcon; + var unSelectedIcon = itemConfig.unselectedIcon; + child = isSelected ? selectedIcon! : unSelectedIcon!; + } + + if (basiceType == TDBottomTabBarBasicType.iconText) { + var selectedIcon = itemConfig.selectedIcon; + var unSelectedIcon = itemConfig.unselectedIcon; + child = Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + isSelected ? selectedIcon! : unSelectedIcon!, + if (centerDistance > 0) + SizedBox( + height: centerDistance, + ), + itemConfig.tabText?.isNotEmpty ?? false ? _textItem( + context, + itemConfig, + isSelected, + TDTheme.of(context).fontBodyExtraSmall!, + ) : Container() + ], + ); + } + + var top = badgeConfig?.badgeTopOffset ?? -2; + var right = badgeConfig?.badgeRightOffset ?? -10; + return Stack( + clipBehavior: Clip.none, + children: [ + child, + Visibility( + visible: badgeConfig?.showBadge ?? false, + child: + Positioned(top: top, right: right, child: _badge(badgeConfig))), + ], + ); + } + + Widget _textItem(BuildContext context, TDBottomTabBarTabConfig config, + bool isSelected, Font font) { + return TDText( + config.tabText, + font: font, + fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, + style: + isSelected ? config.selectTabTextStyle : config.unselectTabTextStyle, + textColor: isSelected + ? TDTheme.of(context).brandNormalColor + : TDTheme.of(context).fontGyColor1, + forceVerticalCenter: true, + ); + } + + _buildItem(BuildContext context) { + var badgeConfig = itemConfig.badgeConfig; + var isInOrOutCapsule = componentType == TDBottomTabBarComponentType.label || + outlineType == TDBottomTabBarOutlineType.capsule; + + var child = Container( + alignment: Alignment.center, + padding: EdgeInsets.only( + top: isInOrOutCapsule ? 3.0 : 2.0, + bottom: isInOrOutCapsule + ? (basiceType == TDBottomTabBarBasicType.iconText ? 0.0 : 1.0) + : 0.0, + ), + color: Colors.transparent, + child: _constructItem(context, badgeConfig, isInOrOutCapsule), + ); + + if (!needInkWell) { + return child; + } + return Material( + color: Colors.transparent, + borderRadius: isInOrOutCapsule ? BorderRadius.circular(24) : null, + child: InkWell( + borderRadius: isInOrOutCapsule ? BorderRadius.circular(24) : null, + splashFactory: InkRipple.splashFactory, + splashColor: selectedBgColor ?? TDTheme.of(context).brandColor1, + highlightColor: selectedBgColor ?? TDTheme.of(context).brandColor1, + onTap: () => handleTap(context), + child: child, + ), + ); + } + + void handleTap(BuildContext context) { + onTap.call(); + + var popUpButtonConfig = itemConfig.popUpButtonConfig; + if (popUpButtonConfig != null) { + Navigator.push( + context, + PopRoute( + child: PopupDialog( + itemWidth - _kDefaultMenuItemWidthShrink, + btnContext: context, + config: popUpButtonConfig.popUpDialogConfig, + items: popUpButtonConfig.items, + onClickMenu: (value) { + popUpButtonConfig.onChanged(value); + }, + ), + )); + } + } +} + +/// 展开项配置 +class TDBottomTabBarPopUpBtnConfig { + TDBottomTabBarPopUpBtnConfig( + {required this.items, required this.onChanged, this.popUpDialogConfig}) + : assert(() { + if (popUpDialogConfig != null) { + if ((popUpDialogConfig.arrowHeight != null && + popUpDialogConfig.arrowHeight! <= 0.0) || + (popUpDialogConfig.arrowWidth != null && + popUpDialogConfig.arrowWidth! <= 0.0)) { + throw FlutterError( + '[TDBottomTabBarPopUpBtnConfig] arrowHeight or arrowHeight can ' + 'not set less than or equal to zero'); + } + } + return true; + }()); + + /// 选项list + final List items; + + /// 统一在 onChanged 中处理各item点击事件 + final ValueChanged onChanged; + + /// 弹窗UI配置 + final TDBottomTabBarPopUpShapeConfig? popUpDialogConfig; +} + +/// 弹窗UI配置 +class TDBottomTabBarPopUpShapeConfig { + TDBottomTabBarPopUpShapeConfig( + {this.popUpWidth, + this.popUpitemHeight = _kDefaultMenuItemHeight, + this.backgroundColor, + this.radius, + this.arrowWidth, + this.arrowHeight}); + + /// 弹窗宽度(不设置,默认为按钮宽度 - 20) + final double? popUpWidth; + + /// 单个选项高度 所有选项等高 不设置则使用默认值 48 + final double? popUpitemHeight; + + /// 弹窗背景颜色 + final Color? backgroundColor; + + /// panel圆角 默认0 + final double? radius; + + /// 箭头宽度 默认13.5 + final double? arrowWidth; + + /// 箭头高度 默认8 + final double? arrowHeight; +} + +/// 弹窗菜单item +class PopUpMenuItem extends StatelessWidget { + const PopUpMenuItem({ + Key? key, + this.itemWidget, + required this.value, + this.alignment = AlignmentDirectional.center, + }) : super(key: key); + + /// 选项widget + final Widget? itemWidget; + + /// 选项值 + final String value; + + /// 对齐方式 + final AlignmentGeometry alignment; + + @override + Widget build(BuildContext context) { + return Container( + constraints: const BoxConstraints(minHeight: _kMenuItemMinHeight), + alignment: alignment, + child: itemWidget ?? + TDText( + value, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w400), + ), + ); + } +} + +class PopRoute extends PopupRoute { + Widget child; + + PopRoute({required this.child}); + + @override + Color? get barrierColor => Colors.transparent; + + @override + bool get barrierDismissible => true; + + @override + String? get barrierLabel => 'popUpMenuBarrierLabel'; + + @override + Widget buildPage(BuildContext context, Animation animation, + Animation secondaryAnimation) { + return child; + } + + @override + Duration get transitionDuration => _kPopupMenuDuration; +} + +class PopupDialog extends StatefulWidget { + /// 按钮context + final BuildContext btnContext; + + /// 点击事件 + final ValueChanged onClickMenu; + + /// 弹窗选项列表 + final List items; + + /// 弹窗配置 + final TDBottomTabBarPopUpShapeConfig? config; + + /// 默认弹窗宽度 + final double defaultPopUpWidth; + + const PopupDialog(this.defaultPopUpWidth, + {Key? key, + required this.btnContext, + required this.onClickMenu, + required this.items, + required this.config}) + : super(key: key); + + @override + PopupDialogState createState() => PopupDialogState(); +} + +class PopupDialogState extends State { + RenderBox? button; + RenderBox? overlay; + RelativeRect? position; + Size? size; + + @override + void initState() { + super.initState(); + button = widget.btnContext.findRenderObject() as RenderBox; + size = button!.size; + overlay = + Overlay.of(widget.btnContext)?.context.findRenderObject() as RenderBox; + position = RelativeRect.fromRect( + Rect.fromPoints( + button!.localToGlobal(Offset.zero, ancestor: overlay), + button!.localToGlobal(Offset.zero, ancestor: overlay), + ), + Offset.zero & overlay!.size, + ); + } + + @override + Widget build(BuildContext context) { + var popUpitemHeight = + widget.config?.popUpitemHeight ?? _kDefaultMenuItemHeight; + var popUpItemWidth = widget.config?.popUpWidth ?? widget.defaultPopUpWidth; + var menuItems = widget.items + .map((e) => GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + widget.onClickMenu(e.value); + Navigator.of(context).pop(); + }, + child: SizedBox( + height: popUpitemHeight, + child: e, + ))) + .toList(); + + return Material( + type: MaterialType.transparency, + child: GestureDetector( + onTap: () => Navigator.of(context).pop(), + child: Stack( + children: [ + Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + color: Colors.transparent, + ), + Positioned( + + /// 这里 -8 是因为widget.btnContext是TDBottomTabBarItemWithBadge的,它在父widget内有8dp的padding + /// -4 是设计稿上箭头和tab有4dp的距离 + top: position!.top - + (popUpitemHeight * widget.items.length + + (widget.config?.arrowHeight ?? _kArrowHeight)) - + 8 - + 4, + right: position!.right - (popUpItemWidth + size!.width) / 2, + child: Container( + width: popUpItemWidth, + height: popUpitemHeight * widget.items.length + + (widget.config?.arrowHeight ?? _kArrowHeight), + decoration: + BoxDecoration(boxShadow: TDTheme.of(context).shadowsTop), + child: CustomPaint( + painter: PanelWithDownArrow(config: widget.config), + child: Container( + alignment: Alignment.topCenter, + height: popUpitemHeight * widget.items.length, + child: Container( + constraints: BoxConstraints( + maxHeight: popUpitemHeight * widget.items.length), + child: Stack( + children: [ + Column(children: menuItems), + Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: List.generate( + widget.items.length - 1, + (index) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0), + child: Divider( + thickness: 0.5, + height: 0.5, + color: TDTheme.of(context).grayColor3, + ), + )), + ) + ], + ), + ), + ), + ), + )) + ], + ), + ), + ); + } +} + +/// 带下箭头的展开panel +class PanelWithDownArrow extends CustomPainter { + TDBottomTabBarPopUpShapeConfig? config; + + PanelWithDownArrow({ + this.config, + }); + + @override + void paint(Canvas canvas, Size size) { + var paint = Paint() + ..isAntiAlias = true + ..color = config?.backgroundColor ?? Colors.white + ..style = PaintingStyle.fill; + var path = Path(); + var panelWidth = size.width; + var panelHeight = size.height - (config?.arrowHeight ?? _kArrowHeight); + + canvas.drawRRect( + RRect.fromRectAndRadius(Rect.fromLTWH(0, 0, panelWidth, panelHeight), + Radius.circular(config?.radius ?? 0.0)), + paint); + + /// 下方箭头 + if (config?.arrowWidth != 0.0 && config?.arrowHeight != 0.0) { + var left = (panelWidth - _kArrowWidth) / 2; + var right = (panelWidth + _kArrowWidth) / 2; + var bottom = panelHeight + _kArrowHeight; + if (config?.arrowWidth != null) { + left = (panelWidth - config!.arrowWidth!) / 2; + right = (panelWidth + config!.arrowWidth!) / 2; + } + if (config?.arrowHeight != null) { + bottom = panelHeight + config!.arrowHeight!; + } + + path.moveTo(left, panelHeight); + path.lineTo(panelWidth / 2, bottom); + path.lineTo(right, panelHeight); + canvas.drawPath(path, paint); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} diff --git a/tdesign-component/lib/src/components/table/td_table.dart b/tdesign-component/lib/src/components/table/td_table.dart new file mode 100644 index 000000000..d2548fc23 --- /dev/null +++ b/tdesign-component/lib/src/components/table/td_table.dart @@ -0,0 +1,776 @@ +import 'dart:math' as math; +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import 'td_table_col.dart'; + +typedef OnCellTap = void Function(int rowIndex, dynamic row, TDTableCol col); +typedef OnScroll = void Function(ScrollController controller); +typedef OnSelect = void Function(List? data); +typedef OnRowSelect = void Function(int index, bool checked); + +class TDTable extends StatefulWidget { + const TDTable({ + super.key, + this.bordered, + required this.columns, + this.data, + this.empty, + this.height, + this.rowHeight, + this.loading = false, + this.loadingWidget, + this.showHeader = true, + this.stripe = false, + this.backgroundColor, + this.width, + this.defaultSort, + this.onCellTap, + this.onScroll, + this.onSelect, + this.onRowSelect, + }); + + /// 是否显示表格边框 + final bool? bordered; + + /// 列配置 + final List columns; + + /// 数据源 + final List? data; + + /// 空表格呈现样式 + final TDTableEmpty? empty; + + /// 表格高度,超出后会出现滚动条 + final double? height; + + /// 行高 + final double? rowHeight; + + /// 加载中状态 + final bool? loading; + + /// 自定义加载中状态 + final Widget? loadingWidget; + + /// 是否显示表头 + final bool? showHeader; + + /// 斑马纹 + final bool? stripe; + + /// 表格背景色 + final Color? backgroundColor; + + /// 表格宽度 + final double? width; + + /// 默认排序 + final String? defaultSort; + + /// 单元格点击事件 + final OnCellTap? onCellTap; + + /// 表格滚动事件 + final OnScroll? onScroll; + + /// 选中行事件 + final OnSelect? onSelect; + + /// 行选择事件 + final OnRowSelect? onRowSelect; + + @override + State createState() => TDTableState(); +} + +class TDTableState extends State { + bool? _sortable; + String? _sortKey; + int _hasChecked = 0; + int _totalSelectable = 0; + bool _checkAll = false; + late TDTableCol _selectableCol; + late List _checkedList; + final _scrollController = ScrollController(); + + /// 获取单元格对齐方式 + Alignment _getVerticalAlign(TDTableColAlign x) { + var xPos = 0.0; + switch (x) { + case TDTableColAlign.left: + xPos = -1; + break; + case TDTableColAlign.center: + xPos = 0; + break; + case TDTableColAlign.right: + xPos = 1; + break; + } + return Alignment(xPos, 0); + } + + /// 过滤列配置 + List _getCol(TDTableColFixed fixed) { + return widget.columns.where((col) => col.fixed == fixed).toList(); + } + + /// 生成表头 + Widget _getTableHeader(BuildContext context) { + var fixedLeftCol = _getCol(TDTableColFixed.left); + var fixedNonCol = _getCol(TDTableColFixed.none); + var fixedRightCol = _getCol(TDTableColFixed.right); + var start = 0; + var fixedLeftCells = [], + cells = [], + fixedRightCells = []; + for (var i = 0; i < fixedLeftCol.length; i++) { + var cell = _getCell(fixedLeftCol[i], true, null, start, i == 0); + if (fixedLeftCol[i].width != null) { + fixedLeftCells.add(SizedBox(width: fixedLeftCol[i].width, child: cell)); + } else { + fixedLeftCells.add(Expanded(flex: 1, child: cell)); + } + start++; + } + start = fixedLeftCol.length; + for (var i = 0; i < fixedNonCol.length; i++) { + var cell = _getCell(fixedNonCol[i], true, null, start, i == 0); + if (fixedNonCol[i].width != null) { + cells.add(SizedBox(width: fixedNonCol[i].width, child: cell)); + } else { + cells.add(Expanded(flex: 1, child: cell)); + } + start++; + } + for (var i = 0; i < fixedRightCol.length; i++) { + var cell = _getCell(fixedRightCol[i], true, null, start, i == 0); + if (fixedRightCol[i].width != null) { + fixedRightCells + .add(SizedBox(width: fixedRightCol[i].width, child: cell)); + } else { + fixedRightCells.add(Expanded(flex: 1, child: cell)); + } + start++; + } + return Row(children: [...fixedLeftCells, ...cells, ...fixedRightCells]); + } + + /// 生成表格内容 + Widget _getTableContent(BuildContext context) { + if (widget.loading ?? false) { + return Align( + alignment: Alignment.center, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 32), + child: widget.loadingWidget ?? + const TDLoading(size: TDLoadingSize.large), + ), + ); + } + if (widget.data == null || widget.data!.isEmpty) { + return _getEmpty('暂无数据'); + } + var cells = []; + var fixedLeftCol = _getCol(TDTableColFixed.left); + var fixedNonCol = _getCol(TDTableColFixed.none); + var fixedRightCol = _getCol(TDTableColFixed.right); + var headerCol = [...fixedLeftCol, ...fixedNonCol, ...fixedRightCol]; + for (var i = 0; i < widget.data!.length; i++) { + var data = widget.data![i]; + var row = []; + for (var j = 0; j < headerCol.length; j++) { + var cell = _getCell( + headerCol[j], + false, + data, + i, + j == (fixedLeftCol.length - 1) || + j == (fixedLeftCol.length + fixedNonCol.length), + ); + if (headerCol[j].width != null) { + row.add(SizedBox(width: headerCol[j].width, child: cell)); + } else { + row.add(Expanded(flex: 1, child: cell)); + } + } + cells.add(Container( + color: (widget.stripe ?? false) && i % 2 == 0 + ? const Color(0xffF3F3F3) + : Colors.white, + child: Row(children: row), + )); + } + return Column( + children: cells, + ); + } + + /// 获取单元格 + Widget _getCell(TDTableCol col, bool isHeader, dynamic data, int index, + bool fixedBorder) { + var title = isHeader ? (col.title ?? '') : (data[col.colKey] ?? ''); + var ellipsis = (isHeader ? col.ellipsisTitle : col.ellipsis) ?? false; + var sortable = col.sortable ?? false; + + // 单元格边框 + var halfBorder = const BorderSide(width: 0.5, color: Color(0xffE7E7E7)); + var doubleBorder = const BorderSide(width: 2, color: Color(0xffE7E7E7)); + var topBorder = BorderSide.none, + rightBorder = BorderSide.none, + leftBorder = BorderSide.none; + var bottomBorder = halfBorder; + if (widget.bordered ?? false) { + rightBorder = halfBorder; + } + if (fixedBorder && col.fixed == TDTableColFixed.left) { + rightBorder = doubleBorder; + } + if (fixedBorder && col.fixed == TDTableColFixed.right) { + leftBorder = doubleBorder; + } + + // 单元格内容 + var text = _getCellText(col, title, ellipsis, isHeader, sortable, index); + var content = text; + if((col.selection ?? false) && col.cellBuilder == null) { + var checkBox; + // 行选择框 + if(_notEmptyData()) { + var enable = col.selectable?.call(index, widget.data?[index]) ?? true; + checkBox = TDCheckbox( + id: 'index:$index', + checked: _checkedList[index], + enable: enable, + customIconBuilder: (context, checked) { + if(checked) { + return Icon(TDIcons.check_rectangle_filled, size: 16, + color: TDTheme.of(context).brandNormalColor); + } + return Icon(TDIcons.rectangle, size: 16, + color: enable ? + TDTheme.of(context).fontGyColor1 : + TDTheme.of(context).fontGyColor3); + }, + onCheckBoxChanged: (checked) { + setState(() { + _checkedList[index] = checked; + if(checked) { + _hasChecked += 1; + } else { + _hasChecked -= 1; + } + _checkAll = _hasChecked == _totalSelectable; + var selectList = []; + for(var i = 0; i < _checkedList.length; i++) { + if(_checkedList[i]) { + selectList.add(widget.data![i]); + } + } + widget.onSelect?.call(selectList); + widget.onRowSelect?.call(index, checked); + }); + }, + ); + } + + // 表头选择框 + if(isHeader) { + checkBox = TDCheckbox( + id: 'header', + checked: _checkAll, + customIconBuilder: (context, checked) { + if(_hasChecked == 0 || _totalSelectable == 0) { + return Icon(TDIcons.rectangle, size: 16, color: TDTheme.of(context).fontGyColor3); + } + var allCheck = _hasChecked >= _totalSelectable; + var halfSelected = _hasChecked > 0 && _hasChecked < _totalSelectable; + return getAllIcon(allCheck, halfSelected); + }, + onCheckBoxChanged: (checked) { + setState(() { + if(!_notEmptyData() && checked) { + _hasChecked = _totalSelectable = 1; + } + _checkAll = checked; + _hasChecked = checked ? _totalSelectable : 0; + for (var i = 0; i < widget.data!.length; i++) { + // 不选中selectable == false的行 + if(_selectableCol.selectable!(i, widget.data![i])) { + _checkedList[i] = checked; + } + } + widget.onSelect?.call(checked ? widget.data : []); + }); + }, + ); + } + + content = Row( + children: [ + checkBox, + text, + ], + ); + } + + // 单元格构建 + var cell = GestureDetector( + onTap: () { + if (isHeader == false) { + widget.onCellTap?.call(index, data, col); + } + }, + child: Container( + decoration: BoxDecoration( + border: Border( + top: topBorder, + right: rightBorder, + bottom: bottomBorder, + left: leftBorder, + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + child: SizedBox( + height: widget.rowHeight ?? 22, + child: Align( + alignment: _getVerticalAlign(col.align!), + child: content, + ), + ), + )), + ); + return cell; + } + + /// 获取单元格内容 + Widget _getCellText(TDTableCol col, String title, bool ellipsis, + bool isHeader, bool sortable, int index) { + var overflow = ellipsis ? TextOverflow.ellipsis : TextOverflow.visible; + var titleWidget = TDText(title, + maxLines: 1, + overflow: overflow, + style: TextStyle( + color: isHeader + ? TDTheme.of(context).fontGyColor3 + : TDTheme.of(context).fontGyColor1, + fontSize: 14, + height: 1, + letterSpacing: 0, + )); + + // 表头(需考虑排序模式) + if (isHeader) { + var selectColor = TDTheme.of(context).brandNormalColor; + var unSelectColor = TDTheme.of(context).fontGyColor3; + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + titleWidget, + Visibility( + visible: isHeader && sortable, + child: Padding( + padding: const EdgeInsets.only(left: 4), + child: GestureDetector( + onTap: () { + setState(() { + if (_sortKey != col.colKey) { + _sortable = true; + } else { + if (_sortable == false) { + _sortable = null; + } else { + _sortable = !(_sortable ?? false); + } + } + _sortKey = col.colKey; + widget.data?.sort((a, b) { + if (_sortable == false) { + return b[col.colKey].compareTo(a[col.colKey]); + } + return a[col.colKey].compareTo(b[col.colKey]); + }); + }); + }, + // 绘制双向箭头 + child: CustomPaint( + size: const Size(16, 16), + painter: ChevronPainter( + upColor: (_sortable == true) && (_sortKey == col.colKey) + ? selectColor + : unSelectColor, + downColor: (_sortable == false) && (_sortKey == col.colKey) + ? selectColor + : unSelectColor, + ), + ), + ), + ), + ) + ], + ); + } + // 自定义单元格内容 + if (col.cellBuilder != null) { + return Builder(builder: (_) => col.cellBuilder!(_, index)); + } + return titleWidget; + } + + /// 获取表格宽度 + double _getColsWidth() { + var width = 0.0; + widget.columns.forEach((col) { + width += (col.width ?? 0); + }); + return width; + } + + bool _notEmptyData() { + return widget.data != null && widget.data!.isNotEmpty; + } + + @override + void initState() { + super.initState(); + _sortKey = widget.defaultSort; + _sortable = widget.defaultSort != null; + _scrollController.addListener(() { + widget.onScroll?.call(_scrollController); + }); + _initCols(); + } + + @override + void didUpdateWidget(covariant TDTable oldWidget) { + super.didUpdateWidget(oldWidget); + _initCols(); + } + + void _initCols() { + _totalSelectable = 0; + _hasChecked = 0; + _checkedList = List.generate((widget.data?.length ?? 0), (index) => false); + var cols = widget.columns.where((col) => col.selection ?? false); + if(cols.length > 1) { + throw FlutterError('selectable column must be only one'); + } + if(widget.data != null && cols.isNotEmpty) { + _selectableCol = cols.first; + var data = widget.data!; + for(var i = 0; i < data.length; i++) { + var check = _selectableCol.checked?.call(i, data[i]) ?? false; + _checkedList[i] = check; + if(check) { + _hasChecked++; + } + if(_selectableCol.selectable?.call(i, data[i]) ?? false) { + _totalSelectable++; + } + } + } + } + + /// 生成固定列表格 + Widget _getFixedTable(BuildContext context) { + // 对列进行分类 + var fixedLeftCol = _getCol(TDTableColFixed.left); + var fixedNonCol = _getCol(TDTableColFixed.none); + var fixedRightCol = _getCol(TDTableColFixed.right); + + // 获取竖向单元格内容 + var fixedLeftTitle = _getCellsText(fixedLeftCol); + var fixedNonTitle = _getCellsText(fixedNonCol); + var fixedRightTitle = _getCellsText(fixedRightCol); + + // 计算单元格宽度(单元格默认平分) + var width = widget.width ?? MediaQuery.of(context).size.width; + var cellWidth = width / widget.columns.length; + + // 生成左侧固定列 + var fixedLeftCols = + _getVerticalCell(fixedLeftCol, fixedLeftTitle, cellWidth); + // 生成非固定列 + var fixedNonCols = _getVerticalCell(fixedNonCol, fixedNonTitle, cellWidth); + // 生成右侧固定列 + var fixedRightCols = + _getVerticalCell(fixedRightCol, fixedRightTitle, cellWidth); + + // 固定列宽度 + var fixedCellsWidth = 0.0; + for(var tableCol in widget.columns) { + if(tableCol.fixed == TDTableColFixed.left || tableCol.fixed == TDTableColFixed.right) { + fixedCellsWidth += (tableCol.width ?? cellWidth); + } + } + + // 计算非固定列宽度 + var fixedNonCellsWidth = 0.0; + for (var col in fixedNonCol) { + // 存在用户自定义宽度 否则使用默认宽度 + fixedNonCellsWidth += col.width ?? cellWidth; + } + + // 非固定列宽度超过剩余宽度 需要开启滚动 + if ((width - fixedCellsWidth) < fixedNonCellsWidth) { + var content = [Row(children: fixedNonCols), _getEmpty('暂无数据')]; + if (widget.loading ?? false) { + content = [ + Row(children: fixedNonCols), + Align( + alignment: Alignment.center, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 32), + child: widget.loadingWidget ?? + const TDLoading(size: TDLoadingSize.large), + ), + ), + ]; + } + return Container( + width: width, + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column(children: [...fixedLeftCols]), + SizedBox( + width: width - fixedCellsWidth, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Column(children: content), + ), + ), + Column( + mainAxisSize: MainAxisSize.min, + children: [...fixedRightCols], + ) + ], + ), + ); + } + var child = Container( + width: width, + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + child: Row( + children: [ + ...fixedLeftCols, + ...fixedNonCols, + ...fixedRightCols, + ], + ), + ); + var placeholder = _getEmpty('暂无数据'); + if (widget.loading ?? false) { + placeholder = Align( + alignment: Alignment.center, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 32), + child: widget.loadingWidget ?? + const TDLoading(size: TDLoadingSize.large), + ), + ); + } + return Container( + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + child: Column(children: [child, placeholder]), + ); + } + + /// 空数据内容 + Widget _getEmpty(String defaultText) { + return Visibility( + visible: widget.data == null || widget.data!.isEmpty, + child: Align( + alignment: Alignment.center, + child: Padding( + padding: const EdgeInsets.only(top: 16, bottom: 38), + child: TDEmpty( + image: Visibility( + visible: widget.empty?.assetUrl != null, + child: _getEmptyImage(), + ), + emptyText: widget.empty?.text ?? defaultText, + ), + ), + ), + ); + } + + TDImage _getEmptyImage() { + var url = widget.empty?.assetUrl ?? ''; + if (url.startsWith('http')) { + return TDImage(imgUrl: url); + } + return TDImage(assetUrl: url); + } + + /// 竖向生成单元格 + List _getVerticalCell( + List cols, List> titles, double cellWidth) { + var rows = []; + for (var i = 0; i < titles.length; i++) { + var cells = []; + for (var j = 0; j < titles[i].length; j++) { + var col = cols[i]; + var cell = _getCell(col, j == 0, j == 0 ? '' : widget.data?[j - 1], i, + i == titles.length - 1); + cells.add(SizedBox(width: col.width ?? cellWidth, child: cell)); + } + rows.add(Column(children: cells)); + } + return rows; + } + + /// 获取每列单元格内容 + List> _getCellsText(List cols) { + var list = >[]; + for (var col in cols) { + var titles = []; + titles.add(col.title ?? ''); + if (widget.loading == false) { + var dataList = []; + for (var i = 0; i < (widget.data?.length ?? 0); i++) { + var data = widget.data![i]; + dataList.add(data[col.colKey] ?? ''); + } + titles..addAll(dataList); + } + list.add(titles); + } + return list; + } + + /// 半选图标 + Widget getAllIcon(bool checked, bool halfSelected) { + return Icon( + checked ? TDIcons.check_rectangle_filled : halfSelected ? TDIcons.minus_rectangle_filled : TDIcons.check_rectangle, + size: 16, + color: (checked || halfSelected) ? TDTheme.of(context).brandNormalColor : TDTheme.of(context).grayColor4 + ); + } + + @override + Widget build(BuildContext context) { + // 固定列 按列生成 + // 非固定列 按行生成 + + // 自定义表格宽度 默认屏幕宽度 + var width = widget.width ?? MediaQuery.of(context).size.width; + var fixedCols = [ + ..._getCol(TDTableColFixed.left), + ..._getCol(TDTableColFixed.right) + ]; + + // 存在固定列 + if (fixedCols.isNotEmpty) { + return _getFixedTable(context); + } + + // 表格超宽 + if (width < _getColsWidth()) { + return Container( + width: width, + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + physics: const ClampingScrollPhysics(), + child: Column( + children: [ + Visibility( + visible: widget.showHeader == true, + child: _getTableHeader(context), + ), + SizedBox( + height: widget.height, + child: SingleChildScrollView( + controller: _scrollController, + physics: const BouncingScrollPhysics(), + child: _getTableContent(context), + ), + ) + ], + ), + ), + ); + } + return Container( + width: width, + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + child: Column( + children: [ + Visibility( + visible: widget.showHeader == true, + child: _getTableHeader(context), + ), + SizedBox( + height: widget.height, + child: SingleChildScrollView( + controller: _scrollController, + physics: const BouncingScrollPhysics(), + child: _getTableContent(context), + ), + ) + ], + ), + ); + } +} + +class ChevronPainter extends CustomPainter { + ChevronPainter({ + required this.upColor, + required this.downColor, + }); + + /// 线条颜色(向上) + final Color upColor; + + /// 线条颜色(向下) + final Color downColor; + + @override + void paint(Canvas canvas, Size size) { + final upPaint = Paint() + ..color = upColor + ..strokeWidth = 1.4 + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + final clientX = size.width; + final clientY = size.height; + final centerX = clientX / 2; + final centerY = clientY / 2; + + // 向上箭头 + final upPath = Path(); + upPath.moveTo(3.6, centerY - 1.8); + upPath.lineTo(centerX, 2); + upPath.lineTo(clientX - 3.6, centerY - 1.8); + + // 向下箭头 + final downPaint = Paint() + ..color = downColor + ..strokeWidth = 1.5 + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + final downPath = Path(); + downPath.moveTo(3.6, centerY + 1.8); + downPath.lineTo(centerX, clientY - 2); + downPath.lineTo(clientX - 3.6, centerY + 1.8); + + canvas.drawPath(upPath, upPaint); + canvas.drawPath(downPath, downPaint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} diff --git a/tdesign-component/lib/src/components/table/td_table_col.dart b/tdesign-component/lib/src/components/table/td_table_col.dart new file mode 100644 index 000000000..9ddb5b672 --- /dev/null +++ b/tdesign-component/lib/src/components/table/td_table_col.dart @@ -0,0 +1,64 @@ +import 'package:flutter/cupertino.dart'; + +enum TDTableColFixed { left, right, none } + +enum TDTableColAlign { left, center, right } + +typedef SelectableFunc = bool Function(int index, dynamic row); +typedef RowCheckFunc = bool Function(int index, dynamic row); + +/// 表格列配置 +class TDTableCol { + TDTableCol({ + this.title, + this.colKey, + this.width, + this.fixed = TDTableColFixed.none, + this.ellipsis, + this.ellipsisTitle, + this.cellBuilder, + this.align = TDTableColAlign.left, + this.sortable = false, + this.selection, + this.selectable, + this.checked, + }); + + /// 行是否显示复选框,自定义列时无效 + bool? selection; + + /// 表头标题 + String? title; + + /// 列取值字段 + String? colKey; + + /// 列宽 + double? width; + + /// 固定列 + TDTableColFixed? fixed; + + /// 列内容超出时是否省略 + bool? ellipsis; + + /// 列标题超出时显示省略内容 + bool? ellipsisTitle; + + /// 自定义列 + IndexedWidgetBuilder? cellBuilder; + + /// 列内容横向对齐方式 + TDTableColAlign? align; + + /// 是否可排序 + bool? sortable; + + /// 当前行CheckBox是否可选,仅selection:true有效 + SelectableFunc? selectable; + + /// 当前行是否选中 + RowCheckFunc? checked; + + double? get widthPx => width; +} \ No newline at end of file diff --git a/tdesign-component/lib/src/components/table/td_table_empty.dart b/tdesign-component/lib/src/components/table/td_table_empty.dart new file mode 100644 index 000000000..238e14425 --- /dev/null +++ b/tdesign-component/lib/src/components/table/td_table_empty.dart @@ -0,0 +1,14 @@ + +/// 空数据状态 +class TDTableEmpty { + TDTableEmpty({ + this.assetUrl, + this.text, + }); + + /// 空状态图片 + String? assetUrl; + + /// 空状态文字 + String? text; +} \ No newline at end of file diff --git a/tdesign-component/lib/src/components/tabs/td_horizontal_tab_bar.dart b/tdesign-component/lib/src/components/tabs/td_horizontal_tab_bar.dart new file mode 100644 index 000000000..65e1535bd --- /dev/null +++ b/tdesign-component/lib/src/components/tabs/td_horizontal_tab_bar.dart @@ -0,0 +1,1574 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; +import 'dart:ui' show lerpDouble; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart' show DragStartBehavior; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +import '../../../tdesign_flutter.dart'; + +const double _kTabHeight = 46.0; +const double _kTextAndIconTabHeight = 72.0; +const double _kStartOffset = 52.0; +class _TabStyle extends AnimatedWidget { + const _TabStyle({ + Key? key, + required Animation animation, + required this.selected, + required this.labelColor, + required this.unselectedLabelColor, + required this.labelStyle, + required this.unselectedLabelStyle, + required this.child, + }) : super(key: key, listenable: animation); + + final TextStyle? labelStyle; + final TextStyle? unselectedLabelStyle; + final bool selected; + final Color? labelColor; + final Color? unselectedLabelColor; + final Widget child; + + @override + Widget build(BuildContext context) { + final themeData = Theme.of(context); + final tabBarTheme = TabBarTheme.of(context); + final animation = listenable as Animation; + + // To enable TextStyle.lerp(style1, style2, value), both styles must have + // the same value of inherit. Force that to be inherit=true here. + final defaultStyle = (labelStyle ?? + tabBarTheme.labelStyle ?? + TextStyle( + height: TDTheme.of(context).fontBodyMedium?.height ?? 1.57, + fontSize: TDTheme.of(context).fontBodyMedium?.size ?? 14)) + .copyWith(inherit: true); + final defaultUnselectedStyle = (unselectedLabelStyle ?? + tabBarTheme.unselectedLabelStyle ?? + labelStyle ?? + TextStyle( + height: TDTheme.of(context).fontBodyMedium?.height ?? 1.57, + fontSize: TDTheme.of(context).fontBodyMedium?.size ?? 14)) + .copyWith(inherit: true); + final textStyle = selected + ? TextStyle.lerp(defaultStyle, defaultUnselectedStyle, animation.value)! + : TextStyle.lerp(defaultUnselectedStyle, defaultStyle, animation.value)!; + + final selectedColor = + labelColor ?? tabBarTheme.labelColor ?? labelStyle?.color ?? TDTheme.of(context).brandNormalColor; + final unselectedColor = unselectedLabelColor ?? + tabBarTheme.unselectedLabelColor ?? + unselectedLabelStyle?.color ?? + TDTheme.of(context).fontGyColor2; // selectedColor.withAlpha(0xB2) // 70% alpha ?? + + final color = selected + ? Color.lerp(selectedColor, unselectedColor, animation.value)! + : Color.lerp(unselectedColor, selectedColor, animation.value)!; + + return DefaultTextStyle( + style: textStyle.copyWith(color: color), + child: IconTheme.merge( + data: IconThemeData( + size: 24.0, + color: color, + ), + child: child, + ), + ); + } +} + +class TDHorizontalTabBar extends StatefulWidget implements PreferredSizeWidget { + /// Creates a material design tab bar. + /// + /// The [tabs] argument must not be null and its length must match the [controller]'s + /// [TabController.length]. + /// + /// If a [TabController] is not provided, then there must be a + /// [DefaultTabController] ancestor. + /// + /// The [indicatorWeight] parameter defaults to 2, and must not be null. + /// + /// The [indicatorPadding] parameter defaults to [EdgeInsets.zero], and must not be null. + /// + /// If [indicator] is not null or provided from [TabBarTheme], + /// then [indicatorWeight], [indicatorPadding], and [indicatorColor] are ignored. + const TDHorizontalTabBar({ + Key? key, + required this.tabs, + this.controller, + this.isScrollable = false, + this.padding, + this.indicatorColor, + this.automaticIndicatorColorAdjustment = true, + this.indicatorWeight = 2.0, + this.indicatorPadding = EdgeInsets.zero, + this.indicator, + this.indicatorSize, + this.labelColor, + this.labelStyle, + this.labelPadding, + this.unselectedLabelColor, + this.unselectedLabelStyle, + this.dragStartBehavior = DragStartBehavior.start, + this.overlayColor, + this.mouseCursor, + this.enableFeedback, + this.onTap, + this.physics, + this.outlineType, + this.backgroundColor, + this.selectedBgColor, + this.unSelectedBgColor, + this.tabAlignment + }) : assert(indicator != null || (indicatorWeight > 0.0)), + super(key: key); + + /// Typically a list of two or more [Tab] widgets. + /// + /// The length of this list must match the [controller]'s [TabController.length] + /// and the length of the [TDHorizontalTabBarView.children] list. + final List tabs; + + /// This widget's selection and animation state. + /// + /// If [TabController] is not provided, then the value of [DefaultTabController.of] + /// will be used. + final TabController? controller; + + /// Whether this tab bar can be scrolled horizontally. + /// + /// If [isScrollable] is true, then each tab is as wide as needed for its label + /// and the entire [TDHorizontalTabBar] is scrollable. Otherwise each tab gets an equal + /// share of the available space. + final bool isScrollable; + + /// The amount of space by which to inset the tab bar. + /// + /// When [isScrollable] is false, this will yield the same result as if you had wrapped your + /// [TDHorizontalTabBar] in a [Padding] widget. When [isScrollable] is true, the scrollable itself is inset, + /// allowing the padding to scroll with the tab bar, rather than enclosing it. + final EdgeInsetsGeometry? padding; + + /// The color of the line that appears below the selected tab. + /// + /// If this parameter is null, then the value of the Theme's indicatorColor + /// property is used. + /// + /// If [indicator] is specified or provided from [TabBarTheme], + /// this property is ignored. + final Color? indicatorColor; + + /// The thickness of the line that appears below the selected tab. + /// + /// The value of this parameter must be greater than zero and its default + /// value is 2.0. + /// + /// If [indicator] is specified or provided from [TabBarTheme], + /// this property is ignored. + final double indicatorWeight; + + /// Padding for indicator. + /// This property will now no longer be ignored even if indicator is declared + /// or provided by [TabBarTheme] + /// + /// For [isScrollable] tab bars, specifying [kTabLabelPadding] will align + /// the indicator with the tab's text for [Tab] widgets and all but the + /// shortest [Tab.text] values. + /// + /// The default value of [indicatorPadding] is [EdgeInsets.zero]. + final EdgeInsetsGeometry indicatorPadding; + + /// Defines the appearance of the selected tab indicator. + /// + /// If [indicator] is specified or provided from [TabBarTheme], + /// the [indicatorColor], and [indicatorWeight] properties are ignored. + /// + /// The default, underline-style, selected tab indicator can be defined with + /// [UnderlineTabIndicator]. + /// + /// The indicator's size is based on the tab's bounds. If [indicatorSize] + /// is [TabBarIndicatorSize.tab] the tab's bounds are as wide as the space + /// occupied by the tab in the tab bar. If [indicatorSize] is + /// [TabBarIndicatorSize.label], then the tab's bounds are only as wide as + /// the tab widget itself. + final Decoration? indicator; + + /// Whether this tab bar should automatically adjust the [indicatorColor]. + /// + /// If [automaticIndicatorColorAdjustment] is true, + /// then the [indicatorColor] will be automatically adjusted to [Colors.white] + /// when the [indicatorColor] is same as [Material.color] of the [Material] parent widget. + final bool automaticIndicatorColorAdjustment; + + /// Defines how the selected tab indicator's size is computed. + /// + /// The size of the selected tab indicator is defined relative to the + /// tab's overall bounds if [indicatorSize] is [TabBarIndicatorSize.tab] + /// (the default) or relative to the bounds of the tab's widget if + /// [indicatorSize] is [TabBarIndicatorSize.label]. + /// + /// The selected tab's location appearance can be refined further with + /// the [indicatorColor], [indicatorWeight], [indicatorPadding], and + /// [indicator] properties. + final TabBarIndicatorSize? indicatorSize; + + /// The color of selected tab labels. + /// + /// Unselected tab labels are rendered with the same color rendered at 70% + /// opacity unless [unselectedLabelColor] is non-null. + /// + /// If this parameter is null, then the color of the [ThemeData.primaryTextTheme]'s + /// bodyText1 text color is used. + final Color? labelColor; + + /// The color of unselected tab labels. + /// + /// If this property is null, unselected tab labels are rendered with the + /// [labelColor] with 70% opacity. + final Color? unselectedLabelColor; + + /// The text style of the selected tab labels. + /// + /// If [unselectedLabelStyle] is null, then this text style will be used for + /// both selected and unselected label styles. + /// + /// If this property is null, then the text style of the + /// [ThemeData.primaryTextTheme]'s bodyText1 definition is used. + final TextStyle? labelStyle; + + /// The padding added to each of the tab labels. + /// + /// If there are few tabs with both icon and text and few + /// tabs with only icon or text, this padding is vertically + /// adjusted to provide uniform padding to all tabs. + /// + /// If this property is null, then kTabLabelPadding is used. + final EdgeInsetsGeometry? labelPadding; + + /// The text style of the unselected tab labels. + /// + /// If this property is null, then the [labelStyle] value is used. If [labelStyle] + /// is null, then the text style of the [ThemeData.primaryTextTheme]'s + /// bodyText1 definition is used. + final TextStyle? unselectedLabelStyle; + + /// Defines the ink response focus, hover, and splash colors. + /// + /// If non-null, it is resolved against one of [MaterialState.focused], + /// [MaterialState.hovered], and [MaterialState.pressed]. + /// + /// [MaterialState.pressed] triggers a ripple (an ink splash), per + /// the current Material Design spec. The [overlayColor] doesn't map + /// a state to [InkResponse.highlightColor] because a separate highlight + /// is not used by the current design guidelines. See + /// https://material.io/design/interaction/states.html#pressed + /// + /// If the overlay color is null or resolves to null, then the default values + /// for [InkResponse.focusColor], [InkResponse.hoverColor], [InkResponse.splashColor] + /// will be used instead. + final MaterialStateProperty? overlayColor; + + /// {@macro flutter.widgets.scrollable.dragStartBehavior} + final DragStartBehavior dragStartBehavior; + + /// The cursor for a mouse pointer when it enters or is hovering over the + /// individual tab widgets. + /// + /// If this property is null, [SystemMouseCursors.click] will be used. + final MouseCursor? mouseCursor; + + /// Whether detected gestures should provide acoustic and/or haptic feedback. + /// + /// For example, on Android a tap will produce a clicking sound and a long-press + /// will produce a short vibration, when feedback is enabled. + /// + /// Defaults to true. + final bool? enableFeedback; + + /// An optional callback that's called when the [TDHorizontalTabBar] is tapped. + /// + /// The callback is applied to the index of the tab where the tap occurred. + /// + /// This callback has no effect on the default handling of taps. It's for + /// applications that want to do a little extra work when a tab is tapped, + /// even if the tap doesn't change the TabController's index. TDHorizontalTabBar [onTap] + /// callbacks should not make changes to the TabController since that would + /// interfere with the default tap handler. + final ValueChanged? onTap; + + /// How the [TDHorizontalTabBar]'s scroll view should respond to user input. + /// + /// For example, determines how the scroll view continues to animate after the + /// user stops dragging the scroll view. + /// + /// Defaults to matching platform conventions. + final ScrollPhysics? physics; + + /// 选项卡样式 + final TDTabBarOutlineType? outlineType; + + /// tabBar背景色 + final Color? backgroundColor; + + /// 被选中背景色 + final Color? selectedBgColor; + + /// 未选中背景色 + final Color? unSelectedBgColor; + + final TabAlignment? tabAlignment; + /// A size whose height depends on if the tabs have both icons and text. + /// + /// [AppBar] uses this size to compute its own preferred size. + @override + Size get preferredSize { + var maxHeight = _kTabHeight; + for (final Widget item in tabs) { + if (item is PreferredSizeWidget) { + final itemHeight = item.preferredSize.height; + maxHeight = math.max(itemHeight, maxHeight); + } + } + return Size.fromHeight(maxHeight + indicatorWeight); + } + + /// Returns whether the [TDHorizontalTabBar] contains a tab with both text and icon. + /// + /// [TDHorizontalTabBar] uses this to give uniform padding to all tabs in cases where + /// there are some tabs with both text and icon and some which contain only + /// text or icon. + bool get tabHasTextAndIcon { + for (final Widget item in tabs) { + if (item is PreferredSizeWidget) { + if (item.preferredSize.height == _kTextAndIconTabHeight) { + return true; + } + } + } + return false; + } + + @override + State createState() => _TDHorizontalTabBarState(); +} + +class _ChangeAnimation extends Animation with AnimationWithParentMixin { + _ChangeAnimation(this.controller); + + final TabController controller; + + @override + Animation get parent => controller.animation!; + + @override + void removeStatusListener(AnimationStatusListener listener) { + if (controller.animation != null) { + super.removeStatusListener(listener); + } + } + + @override + void removeListener(VoidCallback listener) { + if (controller.animation != null) { + super.removeListener(listener); + } + } + + @override + double get value => _indexChangeProgress(controller); +} + +class _IndicatorPainter extends CustomPainter { + _IndicatorPainter({ + required this.controller, + required this.indicator, + required this.indicatorSize, + required this.tabKeys, + required _IndicatorPainter? old, + required this.indicatorPadding, + }) : super(repaint: controller.animation) { + if (old != null) { + saveTabOffsets(old._currentTabOffsets, old._currentTextDirection); + } + } + + final TabController controller; + final Decoration indicator; + final TabBarIndicatorSize? indicatorSize; + final EdgeInsetsGeometry indicatorPadding; + final List tabKeys; + + // _currentTabOffsets and _currentTextDirection are set each time TabBar + // layout is completed. These values can be null when TabBar contains no + // tabs, since there are nothing to lay out. + List? _currentTabOffsets; + TextDirection? _currentTextDirection; + + Rect? _currentRect; + BoxPainter? _painter; + bool _needsPaint = false; + + void markNeedsPaint() { + _needsPaint = true; + } + + void dispose() { + _painter?.dispose(); + } + + void saveTabOffsets(List? tabOffsets, TextDirection? textDirection) { + _currentTabOffsets = tabOffsets; + _currentTextDirection = textDirection; + } + + // _currentTabOffsets[index] is the offset of the start edge of the tab at index, and + // _currentTabOffsets[_currentTabOffsets.length] is the end edge of the last tab. + int get maxTabIndex => _currentTabOffsets!.length - 2; + + double centerOf(int tabIndex) { + assert(_currentTabOffsets != null); + assert(_currentTabOffsets!.isNotEmpty); + assert(tabIndex >= 0); + assert(tabIndex <= maxTabIndex); + return (_currentTabOffsets![tabIndex] + _currentTabOffsets![tabIndex + 1]) / 2.0; + } + + Rect indicatorRect(Size tabBarSize, int tabIndex) { + assert(_currentTabOffsets != null); + assert(_currentTextDirection != null); + assert(_currentTabOffsets!.isNotEmpty); + assert(tabIndex >= 0); + assert(tabIndex <= maxTabIndex); + double tabLeft, tabRight; + switch (_currentTextDirection!) { + case TextDirection.rtl: + tabLeft = _currentTabOffsets![tabIndex + 1]; + tabRight = _currentTabOffsets![tabIndex]; + break; + case TextDirection.ltr: + tabLeft = _currentTabOffsets![tabIndex]; + tabRight = _currentTabOffsets![tabIndex + 1]; + break; + } + + if (indicatorSize == TabBarIndicatorSize.label) { + final tabWidth = tabKeys[tabIndex].currentContext!.size!.width; + final delta = ((tabRight - tabLeft) - tabWidth) / 2.0; + tabLeft += delta; + tabRight -= delta; + } + + final insets = indicatorPadding.resolve(_currentTextDirection); + final rect = Rect.fromLTWH(tabLeft, 0.0, tabRight - tabLeft, tabBarSize.height); + + if (!(rect.size >= insets.collapsedSize)) { + throw FlutterError( + 'indicatorPadding insets should be less than Tab Size\n' + 'Rect Size : ${rect.size}, Insets: ${insets.toString()}', + ); + } + return insets.deflateRect(rect); + } + + @override + void paint(Canvas canvas, Size size) { + _needsPaint = false; + _painter ??= indicator.createBoxPainter(markNeedsPaint); + + final index = controller.index.toDouble(); + final value = controller.animation!.value; + final ltr = index > value; + final from = (ltr ? value.floor() : value.ceil()).clamp(0, maxTabIndex); + final to = (ltr ? from + 1 : from - 1).clamp(0, maxTabIndex); + final fromRect = indicatorRect(size, from); + final toRect = indicatorRect(size, to); + _currentRect = Rect.lerp(fromRect, toRect, (value - from).abs()); + assert(_currentRect != null); + + final configuration = ImageConfiguration( + size: _currentRect!.size, + textDirection: _currentTextDirection, + ); + _painter!.paint(canvas, _currentRect!.topLeft, configuration); + } + + @override + bool shouldRepaint(_IndicatorPainter old) { + return _needsPaint || + controller != old.controller || + indicator != old.indicator || + tabKeys.length != old.tabKeys.length || + (!listEquals(_currentTabOffsets, old._currentTabOffsets)) || + _currentTextDirection != old._currentTextDirection; + } +} + +double _indexChangeProgress(TabController controller) { + final controllerValue = controller.animation!.value; + final previousIndex = controller.previousIndex.toDouble(); + final currentIndex = controller.index.toDouble(); + + // The controller's offset is changing because the user is dragging the + // TabBarView's PageView to the left or right. + if (!controller.indexIsChanging) { + return (currentIndex - controllerValue).abs().clamp(0.0, 1.0); + } + + // The TabController animation's value is changing from previousIndex to currentIndex. + return (controllerValue - currentIndex).abs() / (currentIndex - previousIndex).abs(); +} + +class _TDHorizontalTabBarState extends State { + ScrollController? _scrollController; + TabController? _controller; + _IndicatorPainter? _indicatorPainter; + int? _currentIndex; + late double _tabStripWidth; + late List _tabKeys; + + @override + void initState() { + super.initState(); + // If indicatorSize is TabIndicatorSize.label, _tabKeys[i] is used to find + // the width of tab widget i. See _IndicatorPainter.indicatorRect(). + _tabKeys = widget.tabs.map((Widget tab) => GlobalKey()).toList(); + } + + Decoration get _indicator { + if (widget.indicator != null) { + return widget.indicator!; + } + final tabBarTheme = TabBarTheme.of(context); + if (tabBarTheme.indicator != null) { + return tabBarTheme.indicator!; + } + + var color = widget.indicatorColor ?? Theme.of(context).indicatorColor; + // ThemeData tries to avoid this by having indicatorColor avoid being the + // primaryColor. However, it's possible that the tab bar is on a + // Material that isn't the primaryColor. In that case, if the indicator + // color ends up matching the material's color, then this overrides it. + // When that happens, automatic transitions of the theme will likely look + // ugly as the indicator color suddenly snaps to white at one end, but it's + // not clear how to avoid that any further. + // + // The material's color might be null (if it's a transparency). In that case + // there's no good way for us to find out what the color is so we don't. + // + // TODO(xu-baolin): Remove automatic adjustment to white color indicator + // with a better long-term solution. + // https://github.com/flutter/flutter/pull/68171#pullrequestreview-517753917 + if (widget.automaticIndicatorColorAdjustment && color.value == Material.of(context)?.color?.value) { + color = Colors.white; + } + + return UnderlineTabIndicator( + borderSide: BorderSide( + width: widget.indicatorWeight, + color: color, + ), + ); + } + + // If the TDHorizontalTabBar is rebuilt with a new tab controller, the caller should + // dispose the old one. In that case the old controller's animation will be + // null and should not be accessed. + bool get _controllerIsValid => _controller?.animation != null; + + void _updateTabController() { + final newController = widget.controller ?? DefaultTabController.of(context); + assert(() { + return true; + }()); + + if (newController == _controller) { + return; + } + + if (_controllerIsValid) { + _controller!.animation!.removeListener(_handleTabControllerAnimationTick); + _controller!.removeListener(_handleTabControllerTick); + } + _controller = newController; + if (_controller != null) { + _controller!.animation!.addListener(_handleTabControllerAnimationTick); + _controller!.addListener(_handleTabControllerTick); + _currentIndex = _controller!.index; + } + } + + void _initIndicatorPainter() { + _indicatorPainter = !_controllerIsValid + ? null + : _IndicatorPainter( + controller: _controller!, + indicator: _indicator, + indicatorSize: widget.indicatorSize ?? TabBarTheme.of(context).indicatorSize, + indicatorPadding: widget.indicatorPadding, + tabKeys: _tabKeys, + old: _indicatorPainter, + ); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + assert(debugCheckHasMaterial(context)); + _updateTabController(); + _initIndicatorPainter(); + } + + @override + void didUpdateWidget(TDHorizontalTabBar oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.controller != oldWidget.controller) { + _updateTabController(); + _initIndicatorPainter(); + } else if (widget.indicatorColor != oldWidget.indicatorColor || + widget.indicatorWeight != oldWidget.indicatorWeight || + widget.indicatorSize != oldWidget.indicatorSize || + widget.indicator != oldWidget.indicator) { + _initIndicatorPainter(); + } + + if (widget.tabs.length > oldWidget.tabs.length) { + final delta = widget.tabs.length - oldWidget.tabs.length; + _tabKeys.addAll(List.generate(delta, (int n) => GlobalKey())); + } else if (widget.tabs.length < oldWidget.tabs.length) { + _tabKeys.removeRange(widget.tabs.length, oldWidget.tabs.length); + } + } + + @override + void dispose() { + _indicatorPainter!.dispose(); + if (_controllerIsValid) { + _controller!.animation!.removeListener(_handleTabControllerAnimationTick); + _controller!.removeListener(_handleTabControllerTick); + } + _controller = null; + // We don't own the _controller Animation, so it's not disposed here. + super.dispose(); + } + + int get maxTabIndex => _indicatorPainter!.maxTabIndex; + + double _tabScrollOffset(int index, double viewportWidth, double minExtent, double maxExtent) { + if (!widget.isScrollable) { + return 0.0; + } + var tabCenter = _indicatorPainter!.centerOf(index); + switch (Directionality.of(context)) { + case TextDirection.rtl: + tabCenter = _tabStripWidth - tabCenter; + break; + case TextDirection.ltr: + break; + } + return (tabCenter - viewportWidth / 2.0).clamp(minExtent, maxExtent); + } + + double _tabCenteredScrollOffset(int index) { + final position = _scrollController!.position; + return _tabScrollOffset(index, position.viewportDimension, position.minScrollExtent, position.maxScrollExtent); + } + + double _initialScrollOffset(double viewportWidth, double minExtent, double maxExtent) { + return _tabScrollOffset(_currentIndex!, viewportWidth, minExtent, maxExtent); + } + + void _scrollToCurrentIndex() { + final offset = _tabCenteredScrollOffset(_currentIndex!); + _scrollController!.animateTo(offset, duration: kTabScrollDuration, curve: Curves.ease); + } + + void _scrollToControllerValue() { + final leadingPosition = _currentIndex! > 0 ? _tabCenteredScrollOffset(_currentIndex! - 1) : null; + final middlePosition = _tabCenteredScrollOffset(_currentIndex!); + final trailingPosition = _currentIndex! < maxTabIndex ? _tabCenteredScrollOffset(_currentIndex! + 1) : null; + + final index = _controller!.index.toDouble(); + final value = _controller!.animation!.value; + final double offset; + if (value == index - 1.0) { + offset = leadingPosition ?? middlePosition; + } else if (value == index + 1.0) { + offset = trailingPosition ?? middlePosition; + } else if (value == index) { + offset = middlePosition; + } else if (value < index) { + offset = leadingPosition == null ? middlePosition : lerpDouble(middlePosition, leadingPosition, index - value)!; + } else { + offset = trailingPosition == null ? middlePosition : lerpDouble(middlePosition, trailingPosition, value - index)!; + } + + _scrollController!.jumpTo(offset); + } + + void _handleTabControllerAnimationTick() { + assert(mounted); + if (!_controller!.indexIsChanging && widget.isScrollable) { + // Sync the TDHorizontalTabBar's scroll position with the TDHorizontalTabBarView's PageView. + _currentIndex = _controller!.index; + _scrollToControllerValue(); + } + } + + void _handleTabControllerTick() { + if (_controller!.index != _currentIndex) { + _currentIndex = _controller!.index; + if (widget.isScrollable) { + _scrollToCurrentIndex(); + } + } + setState(() { + // Rebuild the tabs after a (potentially animated) index change + // has completed. + }); + } + + // Called each time layout completes. + void _saveTabOffsets(List tabOffsets, TextDirection textDirection, double width) { + _tabStripWidth = width; + _indicatorPainter?.saveTabOffsets(tabOffsets, textDirection); + } + + void _handleTap(int index) { + assert(index >= 0 && index < widget.tabs.length); + _controller!.animateTo(index); + widget.onTap?.call(index); + } + + Widget _buildStyledTab(Widget child, bool selected, Animation animation) { + return _TabStyle( + animation: animation, + selected: selected, + labelColor: widget.labelColor, + unselectedLabelColor: widget.unselectedLabelColor, + labelStyle: widget.labelStyle, + unselectedLabelStyle: widget.unselectedLabelStyle, + child:child, + ); + } + + BoxDecoration? _getContentDecorateInner(int index) { + if (widget.outlineType == TDTabBarOutlineType.capsule) { + return BoxDecoration( + color: index == _currentIndex + ? (widget.selectedBgColor ?? TDTheme.of(context).brandColor1) + : (widget.unSelectedBgColor ?? TDTheme.of(context).grayColor1), + borderRadius: BorderRadius.circular(32)); + } + return null; + } + + BoxDecoration? _getContentDecorateOuter(int index) { + if (widget.outlineType == TDTabBarOutlineType.capsule) { + return BoxDecoration( + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + ); + } else if (widget.outlineType == TDTabBarOutlineType.card) { + if (index == _currentIndex) { + return BoxDecoration( + color: widget.backgroundColor ?? TDTheme.of(context).whiteColor1, + borderRadius: BorderRadius.only( + topRight: Radius.circular(index + 1 < widget.tabs.length ? 9 : 0), + topLeft: Radius.circular(index > 0 ? 9 : 0))); + } else { + return BoxDecoration( + color: TDTheme.of(context).grayColor1, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(index - 1 == _currentIndex ? 9 : 0), + bottomRight: Radius.circular(index + 1 == _currentIndex ? 9 : 0), + ), + ); + } + } + return null; + } + + Color? _getBackgroundColor(int index) { + if (widget.outlineType == TDTabBarOutlineType.card) { + if (index == _currentIndex) { + return TDTheme.of(context).grayColor1; + } + } + return null; + } + TabAlignment get _defaults { + return widget.isScrollable ? TabAlignment.start : TabAlignment.fill; + } + bool _debugTabAlignmentIsValid(TabAlignment tabAlignment) { + assert(() { + if (widget.isScrollable && tabAlignment == TabAlignment.fill) { + throw FlutterError( + '$tabAlignment is only valid for non-scrollable tab bars.', + ); + } + if (!widget.isScrollable + && (tabAlignment == TabAlignment.start + || tabAlignment == TabAlignment.startOffset)) { + throw FlutterError( + '$tabAlignment is only valid for scrollable tab bars.', + ); + } + return true; + }()); + return true; + } + @override + Widget build(BuildContext context) { + final tabBarTheme = TabBarTheme.of(context); + final TabAlignment effectiveTabAlignment = widget.tabAlignment ?? tabBarTheme.tabAlignment ?? _defaults; + assert(_debugTabAlignmentIsValid(effectiveTabAlignment)); + assert(debugCheckHasMaterialLocalizations(context)); + assert(() { + if (_controller!.length != widget.tabs.length) { + throw FlutterError( + "Controller's length property (${_controller!.length}) does not match the " + "number of tabs (${widget.tabs.length}) present in TDHorizontalTabBar's tabs property.", + ); + } + return true; + }()); + final localizations = MaterialLocalizations.of(context); + if (_controller!.length == 0) { + return Container( + height: _kTabHeight + widget.indicatorWeight, + ); + } + + + + final wrappedTabs = List.generate(widget.tabs.length, (int index) { + const verticalAdjustment = (_kTextAndIconTabHeight - _kTabHeight) / 2.0; + EdgeInsetsGeometry? adjustedPadding; + + final tab = widget.tabs[index]; + if (widget.tabHasTextAndIcon && tab.preferredSize.height == _kTabHeight) { + if (widget.labelPadding != null || tabBarTheme.labelPadding != null) { + adjustedPadding = (widget.labelPadding ?? tabBarTheme.labelPadding!) + .add(const EdgeInsets.symmetric(vertical: verticalAdjustment)); + } else { + adjustedPadding = const EdgeInsets.symmetric(vertical: verticalAdjustment, horizontal: 16.0); + } + } + // tab.size=20; + EdgeInsetsGeometry? capsuleDefaultPadding; + if (widget.outlineType == TDTabBarOutlineType.capsule) { + capsuleDefaultPadding = const EdgeInsets.all(4); + } + return Container( + color: _getBackgroundColor(index), + child: Container( + decoration: _getContentDecorateOuter(index), + child: Center( + heightFactor: 1.0, + child: Padding( + padding: adjustedPadding ?? + widget.labelPadding ?? + capsuleDefaultPadding ?? + tabBarTheme.labelPadding ?? + kTabLabelPadding, + child: KeyedSubtree( + key: _tabKeys[index], + child: Container( + decoration: _getContentDecorateInner(index), + child: widget.tabs[index], + ), + ), + ), + ), + ), + ); + }); + + // If the controller was provided by DefaultTabController and we're part + // of a Hero (typically the AppBar), then we will not be able to find the + // controller during a Hero transition. See https://github.com/flutter/flutter/issues/213. + if (_controller != null) { + final previousIndex = _controller!.previousIndex; + + if (_controller!.indexIsChanging) { + // The user tapped on a tab, the tab controller's animation is running. + assert(_currentIndex != previousIndex); + final Animation animation = _ChangeAnimation(_controller!); + wrappedTabs[_currentIndex!] = _buildStyledTab(wrappedTabs[_currentIndex!], true, animation); + wrappedTabs[previousIndex] = _buildStyledTab(wrappedTabs[previousIndex], false, animation); + } else { + // The user is dragging the TDHorizontalTabBarView's PageView left or right. + final tabIndex = _currentIndex!; + final Animation centerAnimation = _DragAnimation(_controller!, tabIndex); + wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], true, centerAnimation); + if (_currentIndex! > 0) { + final tabIndex = _currentIndex! - 1; + final Animation previousAnimation = ReverseAnimation(_DragAnimation(_controller!, tabIndex)); + wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], false, previousAnimation); + } + if (_currentIndex! < widget.tabs.length - 1) { + final tabIndex = _currentIndex! + 1; + final Animation nextAnimation = ReverseAnimation(_DragAnimation(_controller!, tabIndex)); + wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], false, nextAnimation); + } + } + } + + // Add the tap handler to each tab. If the tab bar is not scrollable, + // then give all of the tabs equal flexibility so that they each occupy + // the same share of the tab bar's overall width. + final tabCount = widget.tabs.length; + for (var index = 0; index < tabCount; index += 1) { + wrappedTabs[index] = Opacity( + opacity: widget.tabs[index].enable ? 1.0 : 0.4, + child: IgnorePointer( + ignoring: !widget.tabs[index].enable, + child: InkWell( + mouseCursor: widget.mouseCursor ?? SystemMouseCursors.click, + onTap: () { + _handleTap(index); + }, + enableFeedback: widget.enableFeedback ?? true, + overlayColor: widget.overlayColor, + child: Container( + padding: widget.outlineType == TDTabBarOutlineType.filled + ? EdgeInsets.only(bottom: widget.indicatorWeight) + : EdgeInsets.zero, + child: Stack( + children: [ + wrappedTabs[index], + Semantics( + selected: index == _currentIndex, + label: localizations.tabLabel(tabIndex: index + 1, tabCount: tabCount), + ), + ], + ), + ), + ), + ), + ); + if (!widget.isScrollable && effectiveTabAlignment == TabAlignment.fill) { + wrappedTabs[index] = Expanded(child: wrappedTabs[index]); + } + } + + Widget tdHorizontalTabBar = CustomPaint( + painter: _indicatorPainter, + child: _TabStyle( + animation: kAlwaysDismissedAnimation, + selected: false, + labelColor: widget.labelColor, + unselectedLabelColor: widget.unselectedLabelColor, + labelStyle: widget.labelStyle, + unselectedLabelStyle: widget.unselectedLabelStyle, + child: _TabLabelBar( + onPerformLayout: _saveTabOffsets, + mainAxisSize: effectiveTabAlignment == TabAlignment.fill ? MainAxisSize.max : MainAxisSize.min, + children: wrappedTabs, + ), + ), + ); + + if (widget.isScrollable) { + final EdgeInsetsGeometry? effectivePadding = effectiveTabAlignment == TabAlignment.startOffset + ? const EdgeInsetsDirectional.only(start: _kStartOffset).add(widget.padding ?? EdgeInsets.zero): widget.padding; + _scrollController ??= _TabBarScrollController(this); + tdHorizontalTabBar = SingleChildScrollView( + dragStartBehavior: widget.dragStartBehavior, + scrollDirection: Axis.horizontal, + controller: _scrollController, + padding: effectivePadding, + physics: widget.physics, + child: tdHorizontalTabBar, + ); + } else if (widget.padding != null) { + tdHorizontalTabBar = Padding( + padding: widget.padding!, + child: tdHorizontalTabBar, + ); + } + + return tdHorizontalTabBar; + } +} + +typedef _LayoutCallback = void Function(List xOffsets, TextDirection textDirection, double width); + +// This class, and TabBarScrollPosition, only exist to handle the case +// where a scrollable TabBar has a non-zero initialIndex. +class _TabBarScrollController extends ScrollController { + _TabBarScrollController(this.tabBar); + + final _TDHorizontalTabBarState tabBar; + + @override + ScrollPosition createScrollPosition(ScrollPhysics physics, ScrollContext context, ScrollPosition? oldPosition) { + return _TabBarScrollPosition( + physics: physics, + context: context, + oldPosition: oldPosition, + tabBar: tabBar, + ); + } +} + +// This class, and TabBarScrollController, only exist to handle the case +// where a scrollable TabBar has a non-zero initialIndex. In that case we can +// only compute the scroll position's initial scroll offset (the "correct" +// pixels value) after the TabBar viewport width and scroll limits are known. +class _TabBarScrollPosition extends ScrollPositionWithSingleContext { + _TabBarScrollPosition({ + required ScrollPhysics physics, + required ScrollContext context, + required ScrollPosition? oldPosition, + required this.tabBar, + }) : super( + physics: physics, + context: context, + initialPixels: null, + oldPosition: oldPosition, + ); + + final _TDHorizontalTabBarState tabBar; + + bool? _initialViewportDimensionWasZero; + + @override + bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) { + var result = true; + if (_initialViewportDimensionWasZero != true) { + // If the viewport never had a non-zero dimension, we just want to jump + // to the initial scroll position to avoid strange scrolling effects in + // release mode: In release mode, the viewport temporarily may have a + // dimension of zero before the actual dimension is calculated. In that + // scenario, setting the actual dimension would cause a strange scroll + // effect without this guard because the super call below would starts a + // ballistic scroll activity. + _initialViewportDimensionWasZero = viewportDimension != 0.0; + correctPixels(tabBar._initialScrollOffset(viewportDimension, minScrollExtent, maxScrollExtent)); + result = false; + } + return super.applyContentDimensions(minScrollExtent, maxScrollExtent) && result; + } +} + +class _TabLabelBarRenderer extends RenderFlex { + _TabLabelBarRenderer({ + List? children, + required Axis direction, + required MainAxisSize mainAxisSize, + required MainAxisAlignment mainAxisAlignment, + required CrossAxisAlignment crossAxisAlignment, + required TextDirection textDirection, + required VerticalDirection verticalDirection, + required this.onPerformLayout, + }) : super( + children: children, + direction: direction, + mainAxisSize: mainAxisSize, + mainAxisAlignment: mainAxisAlignment, + crossAxisAlignment: crossAxisAlignment, + textDirection: textDirection, + verticalDirection: verticalDirection, + ); + + _LayoutCallback onPerformLayout; + + @override + void performLayout() { + super.performLayout(); + // xOffsets will contain childCount+1 values, giving the offsets of the + // leading edge of the first tab as the first value, of the leading edge of + // the each subsequent tab as each subsequent value, and of the trailing + // edge of the last tab as the last value. + var child = firstChild; + final xOffsets = []; + while (child != null) { + final childParentData = child.parentData! as FlexParentData; + xOffsets.add(childParentData.offset.dx); + assert(child.parentData == childParentData); + child = childParentData.nextSibling; + } + assert(textDirection != null); + switch (textDirection!) { + case TextDirection.rtl: + xOffsets.insert(0, size.width); + break; + case TextDirection.ltr: + xOffsets.add(size.width); + break; + } + onPerformLayout(xOffsets, textDirection!, size.width); + } +} + +// This class and its renderer class only exist to report the widths of the tabs +// upon layout. The tab widths are only used at paint time (see _IndicatorPainter) +// or in response to input. +class _TabLabelBar extends Flex { + _TabLabelBar({ + Key? key, + List children = const [], + required this.onPerformLayout, + required super.mainAxisSize, + }) : super( + key: key, + children: children, + direction: Axis.horizontal, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + verticalDirection: VerticalDirection.down, + ); + + final _LayoutCallback onPerformLayout; + + @override + RenderFlex createRenderObject(BuildContext context) { + return _TabLabelBarRenderer( + direction: direction, + mainAxisAlignment: mainAxisAlignment, + mainAxisSize: mainAxisSize, + crossAxisAlignment: crossAxisAlignment, + textDirection: getEffectiveTextDirection(context)!, + verticalDirection: verticalDirection, + onPerformLayout: onPerformLayout, + ); + } + + @override + void updateRenderObject(BuildContext context, _TabLabelBarRenderer renderObject) { + super.updateRenderObject(context, renderObject); + renderObject.onPerformLayout = onPerformLayout; + } +} + +class _DragAnimation extends Animation with AnimationWithParentMixin { + _DragAnimation(this.controller, this.index); + + final TabController controller; + final int index; + + @override + Animation get parent => controller.animation!; + + @override + void removeStatusListener(AnimationStatusListener listener) { + if (controller.animation != null) { + super.removeStatusListener(listener); + } + } + + @override + void removeListener(VoidCallback listener) { + if (controller.animation != null) { + super.removeListener(listener); + } + } + + @override + double get value { + assert(!controller.indexIsChanging); + final controllerMaxValue = (controller.length - 1).toDouble(); + final controllerValue = controller.animation!.value.clamp(0.0, controllerMaxValue); + return (controllerValue - index.toDouble()).abs().clamp(0.0, 1.0); + } +} + +/// A page view that displays the widget which corresponds to the currently +/// selected tab. +/// +/// This widget is typically used in conjunction with a [TDHorizontalTabBar]. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=POtoEH-5l40} +/// +/// If a [TabController] is not provided, then there must be a [DefaultTabController] +/// ancestor. +/// +/// The tab controller's [TabController.length] must equal the length of the +/// [children] list and the length of the [TDHorizontalTabBar.tabs] list. +/// +/// To see a sample implementation, visit the [TabController] documentation. +class TDHorizontalTabBarView extends StatefulWidget { + /// Creates a page view with one child per tab. + /// + /// The length of [children] must be the same as the [controller]'s length. + const TDHorizontalTabBarView({ + Key? key, + required this.children, + this.controller, + this.physics, + this.dragStartBehavior = DragStartBehavior.start, + }) : super(key: key); + + /// This widget's selection and animation state. + /// + /// If [TabController] is not provided, then the value of [DefaultTabController.of] + /// will be used. + final TabController? controller; + + /// One widget per tab. + /// + /// Its length must match the length of the [TDHorizontalTabBar.tabs] + /// list, as well as the [controller]'s [TabController.length]. + final List children; + + /// How the page view should respond to user input. + /// + /// For example, determines how the page view continues to animate after the + /// user stops dragging the page view. + /// + /// The physics are modified to snap to page boundaries using + /// [PageScrollPhysics] prior to being used. + /// + /// Defaults to matching platform conventions. + final ScrollPhysics? physics; + + /// {@macro flutter.widgets.scrollable.dragStartBehavior} + final DragStartBehavior dragStartBehavior; + + @override + State createState() => _TDHorizontalTabBarViewState(); +} + +class _TDHorizontalTabBarViewState extends State { + TabController? _controller; + late PageController _pageController; + late List _children; + late List _childrenWithKey; + int? _currentIndex; + int _warpUnderwayCount = 0; + + // If the TDHorizontalTabBarView is rebuilt with a new tab controller, the caller should + // dispose the old one. In that case the old controller's animation will be + // null and should not be accessed. + bool get _controllerIsValid => _controller?.animation != null; + + void _updateTabController() { + final newController = widget.controller ?? DefaultTabController.of(context); + assert(() { + return true; + }()); + + if (newController == _controller) { + return; + } + + if (_controllerIsValid) { + _controller!.animation!.removeListener(_handleTabControllerAnimationTick); + } + _controller = newController; + if (_controller != null) { + _controller!.animation!.addListener(_handleTabControllerAnimationTick); + } + } + + @override + void initState() { + super.initState(); + _updateChildren(); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _updateTabController(); + _currentIndex = _controller!.index; + _pageController = PageController(initialPage: _currentIndex!); + } + + @override + void didUpdateWidget(TDHorizontalTabBarView oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.controller != oldWidget.controller) { + _updateTabController(); + _currentIndex = _controller!.index; + _pageController.jumpToPage(_currentIndex!); + } + if (widget.children != oldWidget.children && _warpUnderwayCount == 0) { + _updateChildren(); + } + } + + @override + void dispose() { + if (_controllerIsValid) { + _controller!.animation!.removeListener(_handleTabControllerAnimationTick); + } + _controller = null; + // We don't own the _controller Animation, so it's not disposed here. + super.dispose(); + } + + void _updateChildren() { + _children = widget.children; + _childrenWithKey = KeyedSubtree.ensureUniqueKeysForList(widget.children); + } + + void _handleTabControllerAnimationTick() { + if (_warpUnderwayCount > 0 || !_controller!.indexIsChanging) { + return; + } // This widget is driving the controller's animation. + + if (_controller!.index != _currentIndex) { + _currentIndex = _controller!.index; + _warpToCurrentIndex(); + } + } + + Future _warpToCurrentIndex() async { + if (!mounted) { + return Future.value(); + } + + if (_pageController.page == _currentIndex!.toDouble()) { + return Future.value(); + } + + final duration = _controller!.animationDuration; + + if (duration == Duration.zero) { + _pageController.jumpToPage(_currentIndex!); + return Future.value(); + } + + final previousIndex = _controller!.previousIndex; + + if ((_currentIndex! - previousIndex).abs() == 1) { + _warpUnderwayCount += 1; + await _pageController.animateToPage(_currentIndex!, duration: duration, curve: Curves.ease); + _warpUnderwayCount -= 1; + return Future.value(); + } + + assert((_currentIndex! - previousIndex).abs() > 1); + final initialPage = _currentIndex! > previousIndex ? _currentIndex! - 1 : _currentIndex! + 1; + final originalChildren = _childrenWithKey; + setState(() { + _warpUnderwayCount += 1; + + _childrenWithKey = List.of(_childrenWithKey, growable: false); + final temp = _childrenWithKey[initialPage]; + _childrenWithKey[initialPage] = _childrenWithKey[previousIndex]; + _childrenWithKey[previousIndex] = temp; + }); + _pageController.jumpToPage(initialPage); + + await _pageController.animateToPage(_currentIndex!, duration: duration, curve: Curves.ease); + if (!mounted) { + return Future.value(); + } + setState(() { + _warpUnderwayCount -= 1; + if (widget.children != _children) { + _updateChildren(); + } else { + _childrenWithKey = originalChildren; + } + }); + } + + // Called when the PageView scrolls + bool _handleScrollNotification(ScrollNotification notification) { + if (_warpUnderwayCount > 0) { + return false; + } + + if (notification.depth != 0) { + return false; + } + + _warpUnderwayCount += 1; + if (notification is ScrollUpdateNotification && !_controller!.indexIsChanging) { + if ((_pageController.page! - _controller!.index).abs() > 1.0) { + _controller!.index = _pageController.page!.round(); + _currentIndex = _controller!.index; + } + _controller!.offset = (_pageController.page! - _controller!.index).clamp(-1.0, 1.0); + } else if (notification is ScrollEndNotification) { + _controller!.index = _pageController.page!.round(); + _currentIndex = _controller!.index; + if (!_controller!.indexIsChanging) { + _controller!.offset = (_pageController.page! - _controller!.index).clamp(-1.0, 1.0); + } + } + _warpUnderwayCount -= 1; + + return false; + } + + @override + Widget build(BuildContext context) { + assert(() { + if (_controller!.length != widget.children.length) { + throw FlutterError( + "Controller's length property (${_controller!.length}) does not match the " + "number of tabs (${widget.children.length}) present in TDHorizontalTabBar's tabs property.", + ); + } + return true; + }()); + return NotificationListener( + onNotification: _handleScrollNotification, + child: PageView( + dragStartBehavior: widget.dragStartBehavior, + controller: _pageController, + physics: widget.physics == null + ? const PageScrollPhysics().applyTo(const ClampingScrollPhysics()) + : const PageScrollPhysics().applyTo(widget.physics), + children: _childrenWithKey, + ), + ); + } +} + +/// Displays a single circle with the specified border and background colors. +/// +/// Used by [TabPageSelector] to indicate the selected page. +class TabPageSelectorIndicator extends StatelessWidget { + /// Creates an indicator used by [TabPageSelector]. + /// + /// The [backgroundColor], [borderColor], and [size] parameters must not be null. + const TabPageSelectorIndicator({ + Key? key, + required this.backgroundColor, + required this.borderColor, + required this.size, + }) : super(key: key); + + /// The indicator circle's background color. + final Color backgroundColor; + + /// The indicator circle's border color. + final Color borderColor; + + /// The indicator circle's diameter. + final double size; + + @override + Widget build(BuildContext context) { + return Container( + width: size, + height: size, + margin: const EdgeInsets.all(4.0), + decoration: BoxDecoration( + color: backgroundColor, + border: Border.all(color: borderColor), + shape: BoxShape.circle, + ), + ); + } +} + +/// Displays a row of small circular indicators, one per tab. +/// +/// The selected tab's indicator is highlighted. Often used in conjunction with +/// a [TDHorizontalTabBarView]. +/// +/// If a [TabController] is not provided, then there must be a +/// [DefaultTabController] ancestor. +class TabPageSelector extends StatelessWidget { + /// Creates a compact widget that indicates which tab has been selected. + const TabPageSelector({ + Key? key, + this.controller, + this.indicatorSize = 12.0, + this.color, + this.selectedColor, + }) : assert(indicatorSize > 0.0), + super(key: key); + + /// This widget's selection and animation state. + /// + /// If [TabController] is not provided, then the value of + /// [DefaultTabController.of] will be used. + final TabController? controller; + + /// The indicator circle's diameter (the default value is 12.0). + final double indicatorSize; + + /// The indicator circle's fill color for unselected pages. + /// + /// If this parameter is null, then the indicator is filled with [Colors.transparent]. + final Color? color; + + /// The indicator circle's fill color for selected pages and border color + /// for all indicator circles. + /// + /// If this parameter is null, then the indicator is filled with the theme's + /// [ColorScheme.secondary]. + final Color? selectedColor; + + Widget _buildTabIndicator( + int tabIndex, + TabController tabController, + ColorTween selectedColorTween, + ColorTween previousColorTween, + ) { + final Color background; + if (tabController.indexIsChanging) { + // The selection's animation is animating from previousValue to value. + final t = 1.0 - _indexChangeProgress(tabController); + if (tabController.index == tabIndex) { + background = selectedColorTween.lerp(t)!; + } else if (tabController.previousIndex == tabIndex) { + background = previousColorTween.lerp(t)!; + } else { + background = selectedColorTween.begin!; + } + } else { + // The selection's offset reflects how far the TDHorizontalTabBarView has / been dragged + // to the previous page (-1.0 to 0.0) or the next page (0.0 to 1.0). + final offset = tabController.offset; + if (tabController.index == tabIndex) { + background = selectedColorTween.lerp(1.0 - offset.abs())!; + } else if (tabController.index == tabIndex - 1 && offset > 0.0) { + background = selectedColorTween.lerp(offset)!; + } else if (tabController.index == tabIndex + 1 && offset < 0.0) { + background = selectedColorTween.lerp(-offset)!; + } else { + background = selectedColorTween.begin!; + } + } + return TabPageSelectorIndicator( + backgroundColor: background, + borderColor: selectedColorTween.end!, + size: indicatorSize, + ); + } + + @override + Widget build(BuildContext context) { + final fixColor = color ?? Colors.transparent; + final fixSelectedColor = selectedColor ?? Theme.of(context).colorScheme.secondary; + final selectedColorTween = ColorTween(begin: fixColor, end: fixSelectedColor); + final previousColorTween = ColorTween(begin: fixSelectedColor, end: fixColor); + final tabController = controller ?? DefaultTabController.of(context); + final localizations = MaterialLocalizations.of(context); + assert(() { + return true; + }()); + final Animation animation = CurvedAnimation( + parent: tabController!.animation!, + curve: Curves.fastOutSlowIn, + ); + return AnimatedBuilder( + animation: animation, + builder: (BuildContext context, Widget? child) { + return Semantics( + label: localizations.tabLabel(tabIndex: tabController.index + 1, tabCount: tabController.length), + child: Row( + mainAxisSize: MainAxisSize.min, + children: List.generate(tabController.length, (int tabIndex) { + return _buildTabIndicator(tabIndex, tabController, selectedColorTween, previousColorTween); + }).toList(), + ), + ); + }, + ); + } +} diff --git a/tdesign-component/lib/src/components/tabs/td_tab.dart b/tdesign-component/lib/src/components/tabs/td_tab.dart new file mode 100644 index 000000000..3fba9aff2 --- /dev/null +++ b/tdesign-component/lib/src/components/tabs/td_tab.dart @@ -0,0 +1,154 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +enum TDTabSize { large, small } + +enum TDTabOutlineType { + // 填充样式 + filled, + // 胶囊样式 + capsule, + // 卡片 + card +} + +class TDTab extends Tab { + /// 文字内容 + @override + final String? text; + + /// 子widget + @override + final Widget? child; + + /// 图标 + @override + final Widget? icon; + + /// 图标 + final TDBadge? badge; + + /// 图标间距 + @override + final EdgeInsetsGeometry iconMargin; + + /// tab高度 + @override + final double? height; + + /// 中间内容高度 + final double? contentHeight; + + /// 中间内容宽度 + final EdgeInsetsGeometry? textMargin; + + /// 是否可用,默认true + final bool enable; + + /// 选项卡尺寸 + final TDTabSize size; + + ///选项卡样式 + final TDTabOutlineType outlineType; + + @override + const TDTab( + {Key? key, + this.text, + this.child, + this.icon, + this.badge, + this.height, + this.contentHeight, + this.textMargin, + this.size = TDTabSize.small, + this.outlineType = TDTabOutlineType.filled, + this.enable = true, + this.iconMargin = const EdgeInsets.only(bottom: 4.0, right: 4.0)}) + : super(key: key, text: text, child: child, icon: icon, height: height, iconMargin: iconMargin); + + final double _kTabHeight = 48.0; + final double _kTextAndIconTabHeight = 72.0; + + @override + Widget build(BuildContext context) { + final double calculatedHeight; + Widget label; + if (icon == null) { + calculatedHeight = _kTabHeight; + label = _buildLabelText(context); + } else if (text == null && child == null) { + calculatedHeight = _kTabHeight; + label = icon!; + } else { + calculatedHeight = _kTextAndIconTabHeight; + label = Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + icon ?? Container(), + SizedBox( + width: iconMargin.horizontal, + height: iconMargin.vertical, + ), + _buildLabelText(context), + ], + ); + } + if (badge != null) { + label = Stack( + alignment: Alignment.bottomLeft, + children: [ + Container(margin: textMargin, child: label), + Positioned( + child: badge!, + right: 0, + top: 0, + ), + ], + ); + } + var isCapsuleOutlineType = outlineType == TDTabOutlineType.capsule; + + return IgnorePointer( + ignoring: !enable, + child: Container( + alignment: Alignment.center, + margin: isCapsuleOutlineType ? const EdgeInsets.symmetric(horizontal: 16) : null, + height: height ?? calculatedHeight, + child: Center( + widthFactor: 1.0, + child: label, + ), + ), + ); + } + + Widget _buildLabelText(BuildContext context) { + if (child != null) { + return DefaultTextStyle( + child: child!, + style: DefaultTextStyle.of(context).style.copyWith(fontSize: TDTheme.of(context).fontBodySmall?.size ?? 14), + ); + } + return Text( + text!, + softWrap: false, + overflow: TextOverflow.fade, + style: TextStyle(fontSize: _getFontSize(context)), + ); + } + + double _getFontSize(BuildContext context) { + final defaultTextStyle = DefaultTextStyle.of(context); + if (defaultTextStyle.style.fontSize != null) { + return defaultTextStyle.style.fontSize!; + } + if (size == TDTabSize.large) { + return 16.0; + } else { + return 14.0; + } + } +} diff --git a/tdesign-component/lib/src/components/tabs/td_tab_bar.dart b/tdesign-component/lib/src/components/tabs/td_tab_bar.dart new file mode 100644 index 000000000..db9b9b88d --- /dev/null +++ b/tdesign-component/lib/src/components/tabs/td_tab_bar.dart @@ -0,0 +1,305 @@ +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; +import 'td_horizontal_tab_bar.dart'; + +enum TDTabBarOutlineType { + // 填充样式 + filled, + // 胶囊样式 + capsule, + // 卡片 + card +} + +class TDTabBar extends StatefulWidget { + const TDTabBar({ + Key? key, + required this.tabs, + this.controller, + this.decoration, + this.backgroundColor, + this.indicatorColor, + this.indicatorWidth, + this.indicatorHeight, + this.labelColor, + this.unselectedLabelColor, + this.isScrollable = false, + this.unselectedLabelStyle, + this.labelStyle, + this.width, + this.height, + this.indicatorPadding, + this.labelPadding, + this.indicator, + this.physics, + this.onTap, + this.outlineType = TDTabBarOutlineType.filled, + this.showIndicator = false, + this.dividerColor, + this.dividerHeight = 0.5, + this.selectedBgColor, + this.unSelectedBgColor, + this.tabAlignment, + }) : assert( + backgroundColor == null || decoration == null, + 'Cannot provide both a backgroundColor and a decoration\n' + 'To provide both, use "decoration: BoxDecoration(color: color)".', + ), + super(key: key); + + /// tab数组 + final List tabs; // + + /// tab控制器 + final TabController? controller; + + /// tabBar修饰 + final Decoration? decoration; + + /// tabBar背景色,当outlineType为card时控制选中tab颜色 + final Color? backgroundColor; + + /// tabBar下标颜色 + final Color? indicatorColor; + + /// tabBar下标高度 + final double? indicatorHeight; + + /// tabBar下标宽度 + final double? indicatorWidth; + + /// tabBar 已选标签颜色 + final Color? labelColor; + + /// tabBar未选标签颜色 + final Color? unselectedLabelColor; + + /// 是否滚动 + final bool isScrollable; + + /// 已选label字体 + final TextStyle? labelStyle; + + /// unselectedLabel字体 + final TextStyle? unselectedLabelStyle; + + /// tabBar宽度 + final double? width; + + /// tabBar高度 + final double? height; + + /// 引导padding + final EdgeInsets? indicatorPadding; + + /// 自定义引导控件 + final Decoration? indicator; + + /// 是否展示引导控件 + final bool showIndicator; + + /// 自定义滑动 + final ScrollPhysics? physics; + + /// 点击事件 + final Function(int)? onTap; + + /// tab间距 + final EdgeInsetsGeometry? labelPadding; + + /// 选项卡样式 + final TDTabBarOutlineType outlineType; + + /// 分割线颜色 + final Color? dividerColor; + + /// 分割线高度,小于等于0则不展示分割线 + final double dividerHeight; + + /// 被选中背景色,只有outlineType为capsule时有效 + final Color? selectedBgColor; + + /// 未选中背景色,只有outlineType为capsule时有效 + final Color? unSelectedBgColor; + + final TabAlignment? tabAlignment; + @override + State createState() => _TDTabBarState(); +} + +class _TDTabBarState extends State { + /// 默认高度 + static const double _defaultHeight = 48; + + @override + Widget build(BuildContext context) { + return Container( + width: widget.width ?? MediaQuery.of(context).size.width, + height: widget.height ?? _defaultHeight, + decoration: widget.decoration ?? + (widget.outlineType == TDTabBarOutlineType.card + ? BoxDecoration(color: widget.backgroundColor) + : BoxDecoration( + color: widget.backgroundColor, + border: widget.dividerHeight <= 0 + ? null + : Border( + bottom: BorderSide( + color: widget.dividerColor ?? TDTheme.of(context).grayColor3, + width: widget.dividerHeight)))), + child: TDHorizontalTabBar( + physics: widget.physics, + isScrollable: widget.isScrollable, + indicator: widget.indicator ?? _getIndicator(context), + indicatorColor: widget.indicatorColor, + unselectedLabelColor: widget.unselectedLabelColor, + labelColor: widget.labelColor, + labelStyle: widget.labelStyle ?? _getLabelStyle(context), + labelPadding: widget.labelPadding ?? const EdgeInsets.all(8), + unselectedLabelStyle:widget.unselectedLabelStyle ?? _getUnSelectLabelStyle(context), + tabs: widget.tabs, + indicatorPadding: widget.indicatorPadding ?? EdgeInsets.zero, + outlineType: widget.outlineType, + controller: widget.controller, + backgroundColor: widget.backgroundColor, + selectedBgColor: widget.selectedBgColor, + unSelectedBgColor: widget.unSelectedBgColor, + tabAlignment:widget.tabAlignment, + onTap: (index) { + widget.onTap?.call(index); + }, + ), + ); + } + + TextStyle _getUnSelectLabelStyle(BuildContext context) { + return TextStyle( + fontWeight: FontWeight.w400, + // fontSize: TDTheme.of(context).fontBodySmall?.size ?? 14, + color: TDTheme.of(context).fontGyColor2); + } + + TextStyle _getLabelStyle(BuildContext context) { + return TextStyle( + fontWeight: FontWeight.w600, + // fontSize: TDTheme.of(context).fontBodySmall?.size ?? 14, + color: TDTheme.of(context).fontGyColor2); + } + + Decoration _getIndicator(BuildContext context) { + return widget.showIndicator + ? TDTabBarIndicator( + context: context, + indicatorHeight: widget.indicatorHeight, + indicatorWidth: widget.indicatorWidth, + indicatorColor: widget.indicatorColor) + : TDNoneIndicator(); + } +} + +/// TDesign自定义下标 +class TDTabBarIndicator extends Decoration { + final BuildContext? context; + final double? indicatorWidth; + final double? indicatorHeight; + final Color? indicatorColor; + + const TDTabBarIndicator({this.context, this.indicatorWidth, this.indicatorHeight, this.indicatorColor}); + + @override + BoxPainter createBoxPainter([VoidCallback? onChanged]) => _TDTabBarIndicatorPainter(this, onChanged!); +} + +class _TDTabBarIndicatorPainter extends BoxPainter { + /// 下标宽度 + static const double _defaultIndicatorWidth = 16; + + /// 下标高度 + static const double _defaultIndicatorHeight = 3; + + final TDTabBarIndicator decoration; + + final _paint = Paint(); + + _TDTabBarIndicatorPainter(this.decoration, VoidCallback onChanged) { + /// 下标颜色 + _paint.color = decoration.indicatorColor ?? TDTheme.of(decoration.context).brandNormalColor; + _paint.strokeCap = StrokeCap.round; + } + + @override + void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { + canvas.drawLine( + Offset(offset.dx + (configuration.size!.width - _indicatorWidth()) / 2, + configuration.size!.height - _indicatorHeight() / 2), + Offset(offset.dx + (configuration.size!.width + _indicatorWidth()) / 2, + configuration.size!.height - _indicatorHeight() / 2), + _paint..strokeWidth = _indicatorHeight()); + } + + double _indicatorHeight() => decoration.indicatorHeight ?? _defaultIndicatorHeight; + + double _indicatorWidth() => decoration.indicatorWidth ?? _defaultIndicatorWidth; +} + +/// TDesign自定义下标 竖向 +class TDTabBarVerticalIndicator extends Decoration { + final BuildContext? context; + final double? indicatorWidth; + final double? indicatorHeight; + + const TDTabBarVerticalIndicator({this.context, this.indicatorWidth, this.indicatorHeight}); + + @override + BoxPainter createBoxPainter([VoidCallback? onChanged]) => _TDTabBarVerticalIndicatorPainter(this, onChanged!); +} + +class _TDTabBarVerticalIndicatorPainter extends BoxPainter { + /// 下标宽度 + static const double _defaultIndicatorWidth = 1.5; + + /// 下标高度 + static const double _defaultIndicatorHeight = 54; + + final TDTabBarVerticalIndicator decoration; + + final _paint = Paint(); + + _TDTabBarVerticalIndicatorPainter(this.decoration, VoidCallback onChanged) { + /// 下标颜色 + _paint.color = TDTheme.of(decoration.context).brandNormalColor; + _paint.strokeCap = StrokeCap.round; + } + + @override + void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { + canvas.drawLine( + Offset( + 0 + _indicatorWidth() / 2, + offset.dx + (configuration.size!.width - _indicatorHeight()) / 2, + ), + Offset( + 0 + _indicatorWidth() / 2, + offset.dx + (configuration.size!.width + _indicatorHeight()) / 2, + ), + _paint..strokeWidth = _indicatorWidth()); + } + + double _indicatorHeight() => decoration.indicatorHeight ?? _defaultIndicatorHeight; + + double _indicatorWidth() => decoration.indicatorWidth ?? _defaultIndicatorWidth; +} + +/// TDesign不展示下标 +class TDNoneIndicator extends Decoration { + @override + BoxPainter createBoxPainter([VoidCallback? onChanged]) => _TDNoneIndicatorPainter(); +} + +class _TDNoneIndicatorPainter extends BoxPainter { + @override + void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {} +} diff --git a/lib/src/components/tabbar/td_tab_bar_view.dart b/tdesign-component/lib/src/components/tabs/td_tab_bar_view.dart similarity index 100% rename from lib/src/components/tabbar/td_tab_bar_view.dart rename to tdesign-component/lib/src/components/tabs/td_tab_bar_view.dart diff --git a/tdesign-component/lib/src/components/tag/td_select_tag.dart b/tdesign-component/lib/src/components/tag/td_select_tag.dart new file mode 100644 index 000000000..da4ba62a0 --- /dev/null +++ b/tdesign-component/lib/src/components/tag/td_select_tag.dart @@ -0,0 +1,169 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +/// 点击型标签组件,点击时内部更改自身状态 +/// 支持样式:方形/圆角/半圆/带关闭图标 +class TDSelectTag extends StatefulWidget { + const TDSelectTag(this.text, + {this.theme, + this.icon, + this.iconWidget, + this.selectStyle, + this.unSelectStyle, + this.disableSelectStyle, + this.onSelectChanged, + this.isSelected = false, + this.disableSelect = false, + this.size = TDTagSize.medium, + this.padding, + this.forceVerticalCenter = true, + this.isOutline = false, + this.shape = TDTagShape.square, + this.isLight = false, + this.needCloseIcon = false, + this.onCloseTap, + this.fixedWidth, + Key? key}) + : super(key: key); + + /// 标签内容 + final String text; + + /// 主题 + final TDTagTheme? theme; + + /// 图标内容,可随状态改变颜色 + final IconData? icon; + + /// 自定义图标内容,需自处理颜色 + final Widget? iconWidget; + + /// 选中的标签样式 + final TDTagStyle? selectStyle; + + /// 未选中标签样式 + final TDTagStyle? unSelectStyle; + + /// 不可选标签样式 + final TDTagStyle? disableSelectStyle; + + /// 标签点击,选中状态改变时的回调 + final ValueChanged? onSelectChanged; + + /// 是否选中 + final bool isSelected; + + /// 是否禁用选择 + final bool disableSelect; + + /// 标签大小 + final TDTagSize size; + + /// 自定义模式下的间距 + final EdgeInsets? padding; + + /// 是否强制中文文字居中 + final bool forceVerticalCenter; + + /// 是否为描边类型,默认不是 + final bool isOutline; + + /// 标签形状 + final TDTagShape shape; + + /// 是否为浅色 + final bool isLight; + + /// 关闭图标 + final bool needCloseIcon; + + /// 关闭图标点击事件 + final GestureTapCallback? onCloseTap; + + /// 标签的固定宽度 + final double? fixedWidth; + + @override + _TDClickTagState createState() => _TDClickTagState(); +} + +class _TDClickTagState extends State { + bool _isSelected = false; + + @override + void initState() { + super.initState(); + _isSelected = widget.isSelected; + } + + @override + Widget build(BuildContext context) { + Widget result = TDTag( + widget.text, + icon: widget.icon, + iconWidget: widget.iconWidget, + style: _getStyle(), + size: widget.size, + padding: widget.padding, + forceVerticalCenter: widget.forceVerticalCenter, + needCloseIcon: widget.needCloseIcon, + onCloseTap: widget.onCloseTap, + fixedWidth: widget.fixedWidth, + ); + if (!widget.disableSelect) { + result = GestureDetector( + onTap: () { + setState(() { + _isSelected = !_isSelected; + widget.onSelectChanged?.call(_isSelected); + }); + }, + child: result, + ); + } + return result; + } + + TDTagStyle? _getStyle() { + if (widget.disableSelect) { + return _getDisableSelectStyle(); + } + return _isSelected ? _getSelectStyle() : _getUnSelectStyle(); + } + + TDTagStyle _getDisableSelectStyle() { + if (widget.disableSelectStyle != null) { + return widget.disableSelectStyle!; + } + return TDTagStyle.generateDisableSelectStyle(context,widget.isOutline, widget.shape); + } + + TDTagStyle _getSelectStyle() { + if (widget.selectStyle != null) { + return widget.selectStyle!; + } + return widget.isOutline + ? TDTagStyle.generateOutlineStyleByTheme( + context, widget.theme, widget.isLight, widget.shape) + : TDTagStyle.generateFillStyleByTheme( + context, widget.theme, widget.isLight, widget.shape); + } + + TDTagStyle _getUnSelectStyle() { + if (widget.unSelectStyle != null) { + return widget.unSelectStyle!; + } + return widget.isOutline + ? TDTagStyle.generateOutlineStyleByTheme( + context, TDTagTheme.defaultTheme, widget.isLight, widget.shape) + : TDTagStyle.generateFillStyleByTheme( + context, TDTagTheme.defaultTheme, widget.isLight, widget.shape); + } + + @override + void didUpdateWidget(covariant TDSelectTag oldWidget) { + super.didUpdateWidget(oldWidget); + _isSelected = widget.isSelected; + } +} diff --git a/tdesign-component/lib/src/components/tag/td_tag.dart b/tdesign-component/lib/src/components/tag/td_tag.dart new file mode 100644 index 000000000..10c5978bf --- /dev/null +++ b/tdesign-component/lib/src/components/tag/td_tag.dart @@ -0,0 +1,254 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +/// 展示型标签组件,仅展示,内部不可更改自身状态 +/// 支持样式:方形/圆角/半圆/带关闭图标 +/// +class TDTag extends StatelessWidget { + const TDTag(this.text, + {this.theme, + this.icon, + this.iconWidget, + this.textColor, + this.backgroundColor, + this.font, + this.fontWeight, + this.style, + this.size = TDTagSize.medium, + this.padding, + this.forceVerticalCenter = true, + this.isOutline = false, + this.shape = TDTagShape.square, + this.isLight = false, + this.disable = false, + this.needCloseIcon = false, + this.onCloseTap, + this.overflow, + this.fixedWidth, + Key? key}) + : super(key: key); + + /// 标签内容 + final String text; + + /// 主题 + final TDTagTheme? theme; + + /// 图标内容,可随状态改变颜色 + final IconData? icon; + + /// 自定义图标内容,需自处理颜色 + final Widget? iconWidget; + + /// 文字颜色, 优先级高于style的textColor + final Color? textColor; + + /// 背景颜色, 优先级高于style的backgroundColor + final Color? backgroundColor; + + /// 字体尺寸, 优先级高于style的font + final Font? font; + + /// 字体粗细, 优先级高于style的fontWeight + final FontWeight? fontWeight; + + /// 标签样式 + final TDTagStyle? style; + + /// 标签大小 + final TDTagSize size; + + /// 自定义模式下的间距 + final EdgeInsets? padding; + + /// 是否强制中文文字居中 + final bool forceVerticalCenter; + + /// 是否为描边类型,默认不是 + final bool isOutline; + + /// 标签形状 + final TDTagShape shape; + + /// 是否为浅色 + final bool isLight; + + /// 是否为禁用状态 + final bool disable; + + /// 关闭图标 + final bool needCloseIcon; + + /// 文字溢出处理 + final TextOverflow? overflow; + + /// 关闭图标点击事件 + final GestureTapCallback? onCloseTap; + + /// 标签的固定宽度 + final double? fixedWidth; + + @override + Widget build(BuildContext context) { + var innerStyle = _getInnerStyle(context); + + Widget child = TDText( + text, + overflow: overflow ?? TextOverflow.ellipsis, + forceVerticalCenter: forceVerticalCenter, + textColor: textColor ?? innerStyle.getTextColor, + font: font ?? innerStyle.font ?? _getFont(context), + fontWeight: fontWeight ?? innerStyle.fontWeight, + ); + + var innerIcon = getIcon(innerStyle); + if (innerIcon != null || needCloseIcon) { + var children = []; + if (innerIcon != null) { + children.add(Container( + margin: const EdgeInsets.only(right: 4), + width: 14, + height: 14, + child: innerIcon, + )); + } + children.add(child); + if (needCloseIcon) { + children.add(GestureDetector( + onTap: onCloseTap, + child: Container( + margin: const EdgeInsets.only(left: 4), + child: Icon( + TDIcons.close, + color: TDTheme.of(context).fontGyColor3, + size: 14, + ), + ), + )); + } + child = Row( + mainAxisSize: MainAxisSize.min, + children: children, + ); + } + + return Container( + width: fixedWidth, + padding: padding ?? _getPadding(innerStyle.border), + decoration: BoxDecoration( + color: backgroundColor ?? innerStyle.getBackgroundColor, + border: Border.all( + width: innerStyle.border, + color: innerStyle.getBorderColor), + borderRadius: innerStyle.getBorderRadius), + child: Align( + widthFactor: 1, + child: child, + ), + ); + } + + Widget? getIcon(TDTagStyle innerStyle){ + if(iconWidget != null){ + return iconWidget; + } + if(icon != null){ + return RichText( + overflow: TextOverflow.visible, + text: TextSpan( + text: String.fromCharCode(icon!.codePoint), + style: TextStyle( + inherit: false, + color: + innerStyle.textColor, + height: 1, + fontSize: _getIconSize(), + fontFamily: icon!.fontFamily, + package: icon!.fontPackage, + ), + ), + ); + } + return null; + } + + TDTagStyle _getInnerStyle(BuildContext context) { + if (style != null) { + return style!; + } + if(disable){ + return TDTagStyle.generateDisableSelectStyle(context, isOutline, shape); + } + return isOutline + ? TDTagStyle.generateOutlineStyleByTheme( + context, theme, isLight, shape) + : TDTagStyle.generateFillStyleByTheme( + context, theme, isLight, shape); + } + + Font? _getFont(BuildContext context) { + switch (size) { + case TDTagSize.extraLarge: + return TDTheme.of(context).fontBodyMedium; + case TDTagSize.large: + return TDTheme.of(context).fontBodyMedium; + case TDTagSize.small: + return TDTheme.of(context).fontBodyExtraSmall; + default: + return TDTheme.of(context).fontBodySmall; + } + } + + /// 计算padding,需去除描边的宽对,对内描边 + EdgeInsets _getPadding(double border) { + var hPadding = 0.0; + var vPadding = 0.0; + switch (size) { + case TDTagSize.extraLarge: + hPadding = 16; + vPadding = 9; + break; + case TDTagSize.large: + hPadding = 8; + vPadding = 3; + break; + case TDTagSize.medium: + hPadding = 8; + vPadding = 2; + break; + case TDTagSize.small: + hPadding = 6; + vPadding = 2; + break; + default: + return EdgeInsets.zero; + } + if (hPadding >= border) { + hPadding = hPadding - border; + } else { + hPadding = 0; + } + if (vPadding >= border) { + vPadding = vPadding - border; + } else { + vPadding = 0; + } + return EdgeInsets.only(left: hPadding, right: hPadding, top: vPadding, bottom: vPadding); + } + + double _getIconSize() { + switch (size) { + case TDTagSize.extraLarge: + return 16; + case TDTagSize.large: + return 16; + case TDTagSize.medium: + return 14; + case TDTagSize.small: + return 12; + default: + return 14; + } + } +} + diff --git a/tdesign-component/lib/src/components/tag/td_tag_styles.dart b/tdesign-component/lib/src/components/tag/td_tag_styles.dart new file mode 100644 index 000000000..e15567a54 --- /dev/null +++ b/tdesign-component/lib/src/components/tag/td_tag_styles.dart @@ -0,0 +1,211 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +/// Tag展示类型 +enum TDTagTheme { + /// 默认 + defaultTheme, + + /// 常规 + primary, + + /// 警告 + warning, + + /// 危险 + danger, + + /// 成功 + success, +} + +/// 标签尺寸 +enum TDTagSize { extraLarge, large, medium, small, custom } + +/// 标签形状 +enum TDTagShape { square, round, mark } + +/// 标签样式 +class TDTagStyle { + TDTagStyle( + {this.context, + this.textColor, + this.backgroundColor, + this.font, + this.fontWeight, + this.border = 0, + this.borderColor, + this.borderRadius}); + + /// 上下文,方便获取主题内容 + BuildContext? context; + + /// 文字颜色 + Color? textColor; + + /// 背景颜色 + Color? backgroundColor; + + /// 边框颜色 + Color? borderColor; + + /// 圆角 + BorderRadiusGeometry? borderRadius; + + /// 字体尺寸 + Font? font; + + /// 字体粗细 + FontWeight? fontWeight; + + /// 线框粗细 + double border = 0; + + /// 字体颜色,与属性不同名,方便子类自定义处理 + Color get getTextColor => textColor ?? TDTheme.of(context).fontWhColor1; + + /// 背景颜色,与属性不同名,方便子类自定义处理 + Color get getBackgroundColor => + backgroundColor ?? TDTheme.of(context).brandNormalColor; + + /// 线框颜色,与属性不同名,方便子类自定义处理 + Color get getBorderColor => borderColor ?? Colors.transparent; + + /// 圆角,,与属性不同名,方便子类自定义处理 + BorderRadiusGeometry get getBorderRadius => + borderRadius ?? BorderRadius.circular(0); + + /// 根据主题生成填充Tag样式 + TDTagStyle.generateFillStyleByTheme( + BuildContext context, TDTagTheme? theme, bool light, TDTagShape shape) { + this.context = context; + switch (theme) { + case TDTagTheme.primary: + textColor = light + ? TDTheme.of(context).brandNormalColor + : TDTheme.of(context).whiteColor1; + backgroundColor = light + ? TDTheme.of(context).brandLightColor + : TDTheme.of(context).brandNormalColor; + break; + case TDTagTheme.warning: + textColor = light + ? TDTheme.of(context).warningNormalColor + : TDTheme.of(context).whiteColor1; + backgroundColor = light + ? TDTheme.of(context).warningLightColor + : TDTheme.of(context).warningNormalColor; + break; + case TDTagTheme.danger: + textColor = light + ? TDTheme.of(context).errorNormalColor + : TDTheme.of(context).whiteColor1; + backgroundColor = light + ? TDTheme.of(context).errorLightColor + : TDTheme.of(context).errorNormalColor; + break; + case TDTagTheme.success: + textColor = light + ? TDTheme.of(context).successNormalColor + : TDTheme.of(context).whiteColor1; + backgroundColor = light + ? TDTheme.of(context).successLightColor + : TDTheme.of(context).successNormalColor; + break; + case TDTagTheme.defaultTheme: + default: + textColor = TDTheme.of(context).fontGyColor1; + backgroundColor = light + ? TDTheme.of(context).grayColor1 + : TDTheme.of(context).grayColor3; + } + switch(shape){ + case TDTagShape.square: + borderRadius = BorderRadius.circular(TDTheme.of(context).radiusSmall); + break; + case TDTagShape.round: + borderRadius = BorderRadius.circular(TDTheme.of(context).radiusRound); + break; + case TDTagShape.mark: + borderRadius = BorderRadius.only(topRight:Radius.circular(TDTheme.of(context).radiusRound),bottomRight: Radius.circular(TDTheme.of(context).radiusRound)); + break; + } + borderColor = backgroundColor; + } + + /// 根据主题生成描边Tag样式 + TDTagStyle.generateOutlineStyleByTheme( + BuildContext context, TDTagTheme? theme, bool light, TDTagShape shape) { + this.context = context; + switch (theme) { + case TDTagTheme.primary: + borderColor = TDTheme.of(context).brandNormalColor; + textColor = TDTheme.of(context).brandNormalColor; + backgroundColor = light + ? TDTheme.of(context).brandLightColor + : TDTheme.of(context).whiteColor1; + break; + case TDTagTheme.warning: + borderColor = TDTheme.of(context).warningNormalColor; + textColor = TDTheme.of(context).warningNormalColor; + backgroundColor = light + ? TDTheme.of(context).warningLightColor + : TDTheme.of(context).whiteColor1; + break; + case TDTagTheme.danger: + borderColor = TDTheme.of(context).errorNormalColor; + textColor = TDTheme.of(context).errorNormalColor; + backgroundColor = light + ? TDTheme.of(context).errorLightColor + : TDTheme.of(context).whiteColor1; + break; + case TDTagTheme.success: + borderColor = TDTheme.of(context).successNormalColor; + textColor = TDTheme.of(context).successNormalColor; + backgroundColor = light + ? TDTheme.of(context).successLightColor + : TDTheme.of(context).whiteColor1; + break; + case TDTagTheme.defaultTheme: + default: + borderColor = TDTheme.of(context).fontGyColor4; + textColor = TDTheme.of(context).fontGyColor1; + backgroundColor = light + ? TDTheme.of(context).grayColor1 + : TDTheme.of(context).whiteColor1; + } + switch(shape){ + case TDTagShape.square: + borderRadius = BorderRadius.circular(TDTheme.of(context).radiusSmall); + break; + case TDTagShape.round: + borderRadius = BorderRadius.circular(TDTheme.of(context).radiusRound); + break; + case TDTagShape.mark: + borderRadius = BorderRadius.only(topRight:Radius.circular(TDTheme.of(context).radiusRound),bottomRight: Radius.circular(TDTheme.of(context).radiusRound)); + break; + } + border = 1; + } + + /// 根据主题生成禁用Tag样式 + TDTagStyle.generateDisableSelectStyle( + BuildContext context, bool isOutline , TDTagShape shape) { + + borderColor = TDTheme.of(context).grayColor4; + textColor = TDTheme.of(context).fontGyColor4; + backgroundColor = TDTheme.of(context).grayColor2; + switch(shape){ + case TDTagShape.square: + borderRadius = BorderRadius.circular(TDTheme.of(context).radiusSmall); + break; + case TDTagShape.round: + borderRadius = BorderRadius.circular(TDTheme.of(context).radiusRound); + break; + case TDTagShape.mark: + borderRadius = BorderRadius.only(topRight:Radius.circular(TDTheme.of(context).radiusRound),bottomRight: Radius.circular(TDTheme.of(context).radiusRound)); + break; + } + border = isOutline ? 1 : 0; + } +} diff --git a/tdesign-component/lib/src/components/text/td_font_loader.dart b/tdesign-component/lib/src/components/text/td_font_loader.dart new file mode 100644 index 000000000..0c8840405 --- /dev/null +++ b/tdesign-component/lib/src/components/text/td_font_loader.dart @@ -0,0 +1,99 @@ + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../../tdesign_flutter.dart'; + +/// 线上字体加载工具 +class TDFontLoader{ + + /// 缓存字体FontLoader,防止重复加载 + static final _record = {}; + + /// 加载字体资源 + static Future load({required String name, required String fontFamilyUrl}) async { + try { + if(!(_record[name] ?? false)) { + var fontLoader = FontLoader(name); + + fontLoader.addFont(Future(() async { + var uri = Uri.parse(fontFamilyUrl); + var bundle = NetworkAssetBundle(uri); + return await bundle.load(''); + })); + + await fontLoader.load(); + _record[name] = true; + } + return true; + } catch (e) { + print('TDFontLoader load error, name: ${name}, fontFamilyUrl: $fontFamilyUrl}, e: $e'); + } + return false; + } +} + +/// 懒加载FontWidget +class TDFontLoaderWidget extends StatefulWidget { + const TDFontLoaderWidget({Key? key, required this.textWidget, required this.fontFamilyUrl}) : super(key: key); + + final TDText textWidget; + + /// FontFamily的下载地址 + final String fontFamilyUrl; + + @override + State createState() => _TDFontLoaderWidgetState(); +} + +class _TDFontLoaderWidgetState extends State { + bool fontFamilyLoaded = false; + @override + void initState() { + super.initState(); + loadFont(); + } + + void loadFont() async { + if ((widget.textWidget.fontFamily?.fontFamily.isNotEmpty ?? false) + && widget.fontFamilyUrl.isNotEmpty) { + try { + if(await TDFontLoader.load(name: widget.textWidget.fontFamily!.fontFamily, fontFamilyUrl: widget.fontFamilyUrl)){ + setState(() {}); + } + } catch (e) { + print('TDFontLoader loadFont error, data: ${widget.textWidget.data}, fontFamily: ${widget.textWidget.fontFamilyUrl}, e: $e'); + } + } + + fontFamilyLoaded = true; + } + + @override + Widget build(BuildContext context) { + return TDText(widget.textWidget.data, + font:widget.textWidget.font, + fontWeight:widget.textWidget.fontWeight ?? FontWeight.w400, + fontFamily:widget.textWidget.fontFamily, + textColor:widget.textWidget.textColor, + backgroundColor:widget.textWidget.backgroundColor, + isTextThrough:widget.textWidget.isTextThrough , + lineThroughColor:widget.textWidget.lineThroughColor, + package : widget.textWidget.package, + forceVerticalCenter :widget.textWidget.forceVerticalCenter, + style:widget.textWidget.style, + strutStyle:widget.textWidget.strutStyle, + textAlign:widget.textWidget.textAlign, + textDirection:widget.textWidget.textDirection, + locale:widget.textWidget.locale, + softWrap:widget.textWidget.softWrap, + overflow:widget.textWidget.overflow, + textScaleFactor:widget.textWidget.textScaleFactor, + maxLines:widget.textWidget.maxLines, + semanticsLabel:widget.textWidget.semanticsLabel, + textWidthBasis:widget.textWidget.textWidthBasis, + textHeightBehavior:widget.textWidget.textHeightBehavior, + isInFontLoader: true, + ); + } +} diff --git a/tdesign-component/lib/src/components/text/td_text.dart b/tdesign-component/lib/src/components/text/td_text.dart new file mode 100644 index 000000000..c8f79c576 --- /dev/null +++ b/tdesign-component/lib/src/components/text/td_text.dart @@ -0,0 +1,456 @@ +import 'dart:math'; +import 'dart:ui' as ui show TextHeightBehavior; + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../../tdesign_flutter.dart'; +import '../../util/version_util.dart'; + +/// 是否启用强制居中 +var kTextForceVerticalCenterEnable = true; + +/// 是否启用全局字体 +var kTextNeedGlobalFontFamily = true; + +/// 文本控件 +/// 设计原则: +/// 1.为了使用更方便,所以对系统组件进行的扩展,需兼容系统控件所有功能,不能让用户使用TDesign时,因不能满足系统功能而弃用。 +/// 2.非系统已有属性,尽量添加注释 +/// +/// 需求:把一部分在TextStyle中的属性扁平化,放到外层。 +/// 1.暴露系统的所有属性,支持系统所有操作 +/// 2.约束使用主题配置的几种字体 +/// 3.提供转换为系统Text的方法,以使某些系统组件指定接收系统Text时可使用。(Image组件同理) +/// 4.支持自定义TextStyle +/// 5.兼容TextSpan形式 +/// +/// 技巧: +/// 命名参数替换属性的正则: +/// 第一步,把Text中的可选参数拷贝过来,变成如下格式: +/// Text(data, +/// this.style, +/// this.strutStyle, +/// ……) +/// 第二步,正则替换如下: +/// 匹配(前半有默认值,后半无默认值):this\.([a-z|A-Z]+)[ ]*[\=]+[ ]*[a-z|A-Z]+\,|this\.([a-z|A-Z]+)\, +/// 替换:$1$2: this.$1$2, +/// +class TDText extends StatelessWidget { + const TDText( + this.data, { + this.font, + this.fontWeight, + this.fontFamily, + this.textColor = Colors.black, + this.backgroundColor, + this.isTextThrough = false, + this.lineThroughColor = Colors.white, + this.package, + this.style, + this.strutStyle, + this.textAlign, + this.textDirection, + this.locale, + this.softWrap, + this.overflow, + this.textScaleFactor, + this.maxLines, + this.semanticsLabel, + this.textWidthBasis, + this.textHeightBehavior, + this.forceVerticalCenter = false, + this.isInFontLoader = false, + this.fontFamilyUrl, + Key? key, + }) : textSpan = null, + super(key: key); + + /// 富文本构造方法 + const TDText.rich( + this.textSpan, { + this.font, + this.fontWeight, + this.fontFamily, + this.textColor = Colors.black, + this.backgroundColor, + this.isTextThrough = false, + this.lineThroughColor = Colors.white, + this.package, + Key? key, + this.style, + this.strutStyle, + this.textAlign, + this.textDirection, + this.locale, + this.softWrap, + this.overflow, + this.textScaleFactor, + this.maxLines, + this.semanticsLabel, + this.textWidthBasis, + this.textHeightBehavior, + this.forceVerticalCenter = false, + this.isInFontLoader = false, + this.fontFamilyUrl, + }) : data = null, + super(key: key); + + /// 字体尺寸,包含大小size和行高height + final Font? font; + + /// 字体粗细 + final FontWeight? fontWeight; + + /// 字体ttf + final FontFamily? fontFamily; + + /// 文本颜色 + final Color textColor; + + /// 背景颜色 + final Color? backgroundColor; + + /// 字体包名 + final String? package; + + /// 是否是横线穿过样式(删除线) + final bool? isTextThrough; + + /// 删除线颜色,对应TestStyle的decorationColor + final Color? lineThroughColor; + + /// 自定义的TextStyle,其中指定的属性,将覆盖扩展的外层属性 + final TextStyle? style; + + /// 以下系统text属性,释义请参考系统[Text]中注释 + final data; + + final StrutStyle? strutStyle; + + final TextAlign? textAlign; + + final TextDirection? textDirection; + + final Locale? locale; + + final bool? softWrap; + + final TextOverflow? overflow; + + final double? textScaleFactor; + + final int? maxLines; + + final String? semanticsLabel; + + final TextWidthBasis? textWidthBasis; + + final ui.TextHeightBehavior? textHeightBehavior; + + final InlineSpan? textSpan; + + /// 是否强制居中 + final bool forceVerticalCenter; + + /// 是否在FontLoader中使用 + final bool isInFontLoader; + + /// 是否禁用懒加载FontFamily的能力 + final String? fontFamilyUrl; + + @override + Widget build(BuildContext context) { + if (fontFamilyUrl?.isNotEmpty ?? false) { + // 如果设置了Url,则使用TGFontLoader + return TDFontLoaderWidget( + textWidget: this, + fontFamilyUrl: fontFamilyUrl!, + ); + } + if (forceVerticalCenter && kTextForceVerticalCenterEnable) { + var config = getConfiguration(context); + var paddingConfig = config?.paddingConfig; + + var textFont = font ?? TDTheme.of(context).fontBodyLarge ?? Font(size: 16, lineHeight: 24); + var fontSize = style?.fontSize ?? textFont.size; + var height = style?.height ?? textFont.height; + + paddingConfig ??= TDTextPaddingConfig.getDefaultConfig(); + var showHeight = min(paddingConfig.heightRate, height); + return Container( + color: style?.backgroundColor ?? backgroundColor, + height: fontSize * height, + padding: paddingConfig.getPadding(data, fontSize, height), + child: _getRawText(context: context, textStyle: getTextStyle(context, height: showHeight)), + ); + } + var bgColor = style?.backgroundColor ?? backgroundColor; + if (bgColor == null) { + return _getRawText(context: context); + } + return Container( + color: bgColor, + child: _getRawText(context: context), + ); + } + + /// 提取成方法,允许业务定义自己的TDTextConfiguration + TDTextConfiguration? getConfiguration(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + TextStyle? getTextStyle(BuildContext context, {double? height, Color? backgroundColor}) { + var textFont = font ?? TDTheme.of(context).fontBodyLarge ?? Font(size: 16, lineHeight: 24); + + var stylePackage = package ?? fontFamily?.package; + var styleFontFamily = style?.fontFamily ?? fontFamily?.fontFamily; + if (kTextNeedGlobalFontFamily) { + var globalFontFamily = getConfiguration(context)?.globalFontFamily; + styleFontFamily ??= globalFontFamily?.fontFamily; + stylePackage ??= globalFontFamily?.package; + } + var realFontWeight = style?.fontWeight ?? fontWeight; + // Flutter 3.0之后,iOS w500之下字体不生效,需要替换字体 + if (PlatformUtil.isIOS && + (styleFontFamily == null || styleFontFamily.isEmpty) && + realFontWeight != null && + realFontWeight.index <= FontWeight.w500.index) { + stylePackage = null; + styleFontFamily = 'PingFang SC'; + } + return TextStyle( + inherit: style?.inherit ?? true, + color: style?.color ?? textColor, + + /// 不使用系统本身的背景色,因为系统属性存在中英文是,会导致颜色出现阶梯状 + backgroundColor: backgroundColor, + fontSize: style?.fontSize ?? textFont.size, + fontWeight: style?.fontWeight ?? fontWeight ?? textFont.fontWeight, + fontStyle: style?.fontStyle, + letterSpacing: style?.letterSpacing, + wordSpacing: style?.wordSpacing, + textBaseline: style?.textBaseline, + height: height ?? style?.height ?? textFont.height, + leadingDistribution: style?.leadingDistribution, + locale: style?.locale, + foreground: style?.foreground, + background: style?.background, + shadows: style?.shadows, + fontFeatures: style?.fontFeatures, + decoration: style?.decoration ?? (isTextThrough! ? TextDecoration.lineThrough : TextDecoration.none), + decorationColor: style?.decorationColor ?? lineThroughColor, + decorationStyle: style?.decorationStyle, + decorationThickness: style?.decorationThickness, + debugLabel: style?.debugLabel, + // 如果需要字体懒加载,则清空fontFamily + fontFamily: styleFontFamily, + fontFamilyFallback: style?.fontFamilyFallback, + package: isInFontLoader ? null : stylePackage, + ); + } + + /// 获取系统原始Text,以便使用到只能接收系统Text组件的地方 + /// 转化为系统原始Text后,将失去padding和background属性 + Text getRawText({required BuildContext context}) { + return _getRawText(context: context, backgroundColor: backgroundColor); + } + + Text _getRawText({required BuildContext context, TextStyle? textStyle, Color? backgroundColor}) { + return textSpan == null + ? Text( + data, + key: key, + style: textStyle ?? getTextStyle(context, backgroundColor: backgroundColor), + strutStyle: strutStyle, + textAlign: textAlign, + textDirection: textDirection, + locale: locale, + softWrap: softWrap, + overflow: overflow, + textScaleFactor: textScaleFactor, + maxLines: maxLines, + semanticsLabel: semanticsLabel, + textWidthBasis: textWidthBasis, + textHeightBehavior: textHeightBehavior, + ) + : Text.rich( + textSpan!, + style: textStyle ?? getTextStyle(context, backgroundColor: backgroundColor), + strutStyle: strutStyle, + textAlign: textAlign, + textDirection: textDirection, + locale: locale, + softWrap: softWrap, + overflow: overflow, + textScaleFactor: textScaleFactor, + maxLines: maxLines, + semanticsLabel: semanticsLabel, + textWidthBasis: textWidthBasis, + textHeightBehavior: textHeightBehavior, + ); + } +} + +/// TextSpan的TDesign扩展,将部分TextStyle中的参数扁平化。 +class TDTextSpan extends TextSpan { + /// 构造参数,扩展参数释义可参考[TDText]中字段注释 + TDTextSpan({ + BuildContext? context, // 如果未设置font,且不想使用默认的fontBodyLarge尺寸时,需设置context,否则可省略 + Font? font, + FontWeight? fontWeight, + FontFamily? fontFamily, + Color textColor = Colors.black, + bool? isTextThrough = false, + Color? lineThroughColor = Colors.white, + String? package, + String? text, + List? children, + TextStyle? style, + GestureRecognizer? recognizer, + MouseCursor? mouseCursor, + PointerEnterEventListener? onEnter, + PointerExitEventListener? onExit, + String? semanticsLabel, + }) : super( + text: text, + children: children, + style: _getTextStyle( + context, style, font, fontWeight, fontFamily, textColor, isTextThrough, lineThroughColor, package), + recognizer: recognizer, + mouseCursor: mouseCursor, + onEnter: onEnter, + onExit: onExit, + semanticsLabel: semanticsLabel, + ); + + static TextStyle? _getTextStyle( + BuildContext? context, + TextStyle? style, + Font? font, + FontWeight? fontWeight, + FontFamily? fontFamily, + Color textColor, + bool? isTextThrough, + Color? lineThroughColor, + String? package, + ) { + var textFont = font ?? TDTheme.of(context).fontBodyLarge ?? Font(size: 16, lineHeight: 24); + return TextStyle( + inherit: style?.inherit ?? true, + color: style?.color ?? textColor, + backgroundColor: style?.backgroundColor, + fontSize: style?.fontSize ?? textFont.size, + fontWeight: style?.fontWeight ?? fontWeight ?? textFont.fontWeight, + fontStyle: style?.fontStyle, + letterSpacing: style?.letterSpacing, + wordSpacing: style?.wordSpacing, + textBaseline: style?.textBaseline, + height: style?.height ?? textFont.height, + leadingDistribution: style?.leadingDistribution, + locale: style?.locale, + foreground: style?.foreground, + background: style?.background, + shadows: style?.shadows, + fontFeatures: style?.fontFeatures, + decoration: style?.decoration ?? (isTextThrough! ? TextDecoration.lineThrough : TextDecoration.none), + decorationColor: style?.decorationColor ?? lineThroughColor, + decorationStyle: style?.decorationStyle, + decorationThickness: style?.decorationThickness, + debugLabel: style?.debugLabel, + fontFamily: style?.fontFamily ?? fontFamily?.fontFamily, + fontFamilyFallback: style?.fontFamilyFallback, + package: package, + ); + } +} + +/// 存储可以自定义TDText居中算法数据的内部控件 +class TDTextConfiguration extends InheritedWidget { + /// forceVerticalCenter=true时,内置padding配置 + final TDTextPaddingConfig? paddingConfig; + + /// 全局字体,kTextNeedGlobalFontFamily=true时生效 + final FontFamily? globalFontFamily; + + const TDTextConfiguration({Key? key, required Widget child, this.paddingConfig, this.globalFontFamily}) + : super(key: key, child: child); + + @override + bool updateShouldNotify(covariant TDTextConfiguration oldWidget) { + return paddingConfig != oldWidget.paddingConfig; + } +} + +/// 通过Padding自定义TDText居中算法 +class TDTextPaddingConfig { + static TDTextPaddingConfig? _defaultConfig; + static final Map _cacheMap = {}; + + /// 获取默认配置 + static TDTextPaddingConfig getDefaultConfig() { + _defaultConfig ??= TDTextPaddingConfig(); + return _defaultConfig!; + } + + /// 获取padding + EdgeInsetsGeometry getPadding(String? data, double fontSize, double height) { + var cache = _cacheMap[fontSize]?[height]; + if (cache != null) { + return cache; + } + var paddingFont = fontSize * paddingRate; + var paddingLeading; + if (height < heightRate) { + paddingLeading = 0; + } else { + if (PlatformUtil.isIOS || PlatformUtil.isAndroid) { + paddingLeading = (height * 0.5 - paddingExtraRate) * fontSize; + } else { + paddingLeading = 0; + } + } + var paddingTop = paddingFont + paddingLeading; + if (paddingTop < 0) { + paddingTop = 0; + } + var padding = EdgeInsets.only(top: paddingTop); + + // 记录缓存 + var heightMap = _cacheMap[fontSize]; + if (heightMap == null) { + heightMap = {}; + _cacheMap[fontSize] = heightMap; + } + heightMap[height] = padding; + return padding; + } + + /// 以多个汉字测量计算的平均值,Android为Pixel 4模拟器,iOS为iphone 8 plus 模拟器 + double get paddingRate { + if (VersionUtil.isAfterThen('3.2.0')) { + // Dart 3.2.0之后,文字渲染高度有改变. + return PlatformUtil.isWeb + ? 29 / 128 + : PlatformUtil.isAndroid + ? -20 / 128 + : PlatformUtil.isOhos + ? 43 / 128 + : -10 / 128; + } + return PlatformUtil.isWeb + ? 3 / 8 + : PlatformUtil.isAndroid + ? -7 / 128 + : PlatformUtil.isOhos + ? 43 / 128 + : 0; + } + + /// 以多个汉字测量计算的平均值,Android为Pixel 4模拟器,iOS为iphone 8 plus 模拟器 + double get paddingExtraRate => PlatformUtil.isAndroid ? 115 / 256 : 97 / 240; + + /// height比率,因为设置1时,Android文字可能显示不全,默认为1.1 + double get heightRate => PlatformUtil.isAndroid ? 1.1 : 1; +} diff --git a/tdesign-component/lib/src/components/textarea/td_textarea.dart b/tdesign-component/lib/src/components/textarea/td_textarea.dart new file mode 100644 index 000000000..128876a39 --- /dev/null +++ b/tdesign-component/lib/src/components/textarea/td_textarea.dart @@ -0,0 +1,434 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../../theme/basic.dart'; +import '../../theme/td_colors.dart'; +import '../../theme/td_font_family.dart'; +import '../../theme/td_fonts.dart'; +import '../../theme/td_radius.dart'; +import '../../theme/td_spacers.dart'; +import '../../theme/td_theme.dart'; +import '../input/input_view.dart'; +import '../input/td_input.dart'; +import '../text/td_text.dart'; + +enum TDTextareaLayout { vertical, horizontal } + +/// 用于多行文本信息输入 +class TDTextarea extends StatefulWidget { + const TDTextarea({ + Key? key, + this.width, + this.textStyle, + this.backgroundColor = Colors.white, + this.decoration, + this.labelStyle, + this.required, + this.readOnly = false, + this.autofocus = false, + this.onEditingComplete, + this.onSubmitted, + this.hintText, + this.inputType, + this.onChanged, + this.inputFormatters, + this.inputDecoration, + this.maxLines, + this.minLines = 4, + this.focusNode, + this.controller, + this.cursorColor, + this.hintTextStyle, + this.labelWidget, + this.textInputBackgroundColor, + this.size = TDInputSize.large, + this.maxLength, + this.maxLengthEnforcement, + this.allowInputOverMax = false, + this.additionInfo = '', + this.additionInfoColor, + this.textAlign, + this.label, + this.indicator = false, + this.layout = TDTextareaLayout.horizontal, + this.autosize, + this.labelIcon, + this.labelWidth, + this.margin, + this.padding, + this.textareaDecoration, + this.bordered, + this.showBottomDivider=true + }) : super(key: key); + + /// 输入框宽度 + final double? width; + + /// 输入框背景色 + final Color? backgroundColor; + + /// 输入框样式(包括标签) + final Decoration? decoration; + + /// 输入框样式(不包括标签) + final Decoration? textareaDecoration; + + /// 输入框标题 + final String? label; + + /// 输入框标题图标 + final Widget? labelIcon; + + /// 输入框标题宽度 + final double? labelWidth; + + /// label组件,支持自定义 + final Widget? labelWidget; + + /// 是否必填标志(红色*) + final bool? required; + + /// 是否只读 + final bool? readOnly; + + /// 提示文案 + final String? hintText; + + /// 键盘类型,数字、字母 + final TextInputType? inputType; + + /// 输入文本变化时回调 + final ValueChanged? onChanged; + + /// 显示输入内容,如限制长度(LengthLimitingTextInputFormatter(6)) + final List? inputFormatters; + + /// controller 用户获取或者赋值输入内容 + final TextEditingController? controller; + + /// 最大输入行数 + final int? maxLines; + + /// 最小输入行数 + final int? minLines; + + /// 获取或者取消焦点使用 + final FocusNode? focusNode; + + /// 是否自动获取焦点 + final bool? autofocus; + + /// 点击键盘完成按钮时触发的回调 + final VoidCallback? onEditingComplete; + + /// 点击键盘完成按钮时触发的回调, 参数值为输入的内容 + final ValueChanged? onSubmitted; + + /// 自定义输入框TextField组件样式 + final InputDecoration? inputDecoration; + + /// 文本颜色 + final TextStyle? textStyle; + + /// 提示文本颜色,默认为文本颜色 + final TextStyle? hintTextStyle; + + /// 文本框背景色 + final Color? textInputBackgroundColor; + + /// 游标颜色 + final Color? cursorColor; + + /// 输入框规格 + final TDInputSize? size; + + /// 最大字数限制 + final int? maxLength; + + /// 如何执行输入长度限制 + final MaxLengthEnforcement? maxLengthEnforcement; + + /// 超出[maxLength]之后是否还允许输入 + final bool? allowInputOverMax; + + /// 错误提示信息 + final String? additionInfo; + + /// 错误提示颜色 + final Color? additionInfoColor; + + /// 文字对齐方向 + final TextAlign? textAlign; + + /// 左侧标签文本样式 + final TextStyle? labelStyle; + + /// 否显示文本计数器,如 0/140(必须设置maxLength) + final bool? indicator; + + /// 标题输入框布局方式。可选项:vertical/horizontal + final TDTextareaLayout? layout; + + /// 是否自动增高,值为 true 时,[maxLines]不生效 + final bool? autosize; + + /// 外边距 + final EdgeInsetsGeometry? margin; + + /// 内边距 + final EdgeInsetsGeometry? padding; + + /// 是否显示外边框 + final bool? bordered; + + /// 边框外部下划线 + final bool? showBottomDivider; + @override + _TDTextareaState createState() => _TDTextareaState(); +} + +class _TDTextareaState extends State { + final _hasFocus = ValueNotifier(false); + late FocusNode _focusNode; + + @override + void initState() { + super.initState(); + _focusNode = widget.focusNode ?? FocusNode(); + _focusNode.addListener(_focusListener); + } + + @override + void dispose() { + _focusNode.removeListener(_focusListener); + if (widget.focusNode == null) { + _focusNode.dispose(); + } + super.dispose(); + } + + void _focusListener() { + _hasFocus.value = _focusNode.hasFocus; + } + + @override + Widget build(BuildContext context) { + var padding = _getInputPadding(context); + var textareaView = _getTextareaView(context, _getInputView(context), _getIndicatorView(context)); + var container = _getContainer(context, _getLabelView(context), textareaView); + if (widget.bordered == true || widget.decoration != null) { + return container; + } + return Stack( + children: [ + container, + if(widget.showBottomDivider!=null && widget!.showBottomDivider==true) + Positioned( + bottom: 0, + left: padding, + right: 0, + child: Container( + height: 0.5, + color: TDTheme.of(context).grayColor3, + ), + ), + ], + ); + } + + Widget _getLabelView(BuildContext context) { + var padding = _getInputPadding(context); + var isHorizontal = widget.layout == TDTextareaLayout.horizontal; + var fontSize = isHorizontal ? TDTheme.of(context).fontBodyLarge?.size : TDTheme.of(context).fontBodyMedium?.size; + if ((widget.label == null || widget.label == '') && widget.labelIcon == null && widget.labelWidget == null) { + return const SizedBox.shrink(); + } + return Container( + width: widget.labelWidth, + padding: isHorizontal ? EdgeInsets.only(right: padding) : EdgeInsets.only(bottom: TDTheme.of(context).spacer8), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + widget.labelIcon ?? const SizedBox.shrink(), + widget.label != null && widget.label != '' + ? Flexible( + child: Padding( + padding: EdgeInsets.only(left: widget.labelIcon != null ? TDTheme.of(context).spacer4 : 0), + child: TDText( + widget.label!, + maxLines: isHorizontal ? 2 : 1, + overflow: TextOverflow.ellipsis, + style: widget.labelStyle ?? TextStyle(fontSize: fontSize), + ), + ), + ) + : const SizedBox.shrink(), + widget.labelWidget ?? const SizedBox.shrink(), + widget.required == true + ? Padding( + padding: EdgeInsets.only(left: TDTheme.of(context).spacer4), + child: TDText( + '*', + style: TextStyle(color: TDTheme.of(context).errorColor6, fontSize: fontSize, height: 1.3), + ), + ) + : const SizedBox.shrink(), + ], + ), + ); + } + + Widget _getInputView(BuildContext context) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: const BoxConstraints(minHeight: 24), // 设置最小高度为24 + child: TDInputView( + textStyle: widget.textStyle ?? TextStyle(color: TDTheme.of(context).fontGyColor1), + readOnly: widget.readOnly ?? false, + autofocus: widget.autofocus ?? false, + onEditingComplete: widget.onEditingComplete, + onSubmitted: widget.onSubmitted, + hintText: widget.hintText, + inputType: widget.inputType, + textAlign: widget.textAlign, + onChanged:(val){ + setState(() { + }); + if(widget.onChanged!=null){ + widget.onChanged!(val); + } + }, + inputFormatters: [ + ...(widget.inputFormatters ?? []), + ...(widget.maxLength != null && !(widget.allowInputOverMax ?? false) + ? [ + LengthLimitingTextInputFormatter( + widget.maxLength, + maxLengthEnforcement: widget.maxLengthEnforcement, + ) + ] + : []) + ], + inputDecoration: widget.inputDecoration, + minLines: widget.minLines, + maxLines: widget.autosize == true ? null : widget.maxLines, + focusNode: _focusNode, + isCollapsed: true, + hintTextStyle: widget.hintTextStyle ?? + TextStyle( + color: widget.readOnly == true ? TDTheme.of(context).fontGyColor4 : TDTheme.of(context).fontGyColor3), + cursorColor: widget.cursorColor, + textInputBackgroundColor: widget.textInputBackgroundColor, + controller: widget.controller, + contentPadding: EdgeInsets.zero, + ), + ), + ); + } + + Widget _getIndicatorView(BuildContext context) { + var padding = _getInputPadding(context); + var showAdditionInfo = widget.additionInfo != '' && widget.additionInfo != null; + var showIndicator = widget.indicator == true && widget.maxLength != null; + var widgetList = []; + if (showAdditionInfo) { + widgetList.add( + Expanded( + child: ValueListenableBuilder( + valueListenable: _hasFocus, + builder: (context, value, child) { + return Opacity( + opacity: value ? 0 : 1, + child: TDText( + widget.additionInfo!, + style: TextStyle( + fontSize: TDTheme.of(context).fontBodySmall?.size, + color: widget.additionInfoColor ?? TDTheme.of(context).fontGyColor3, + ), + ), + ); + }, + ), + ), + ); + } + if (showAdditionInfo && showIndicator) { + widgetList.add(SizedBox(width: padding)); + } + if (showIndicator) { + widgetList.add(TDText( + '${widget.controller?.text.length ?? 0}/${widget.maxLength}', + style: TextStyle(fontSize: TDTheme.of(context).fontBodySmall?.size, color: TDTheme.of(context).fontGyColor3), + )); + } + return Visibility( + visible: showIndicator || showAdditionInfo, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.end, + children: widgetList, + ), + ); + } + + Widget _getTextareaView(BuildContext context, Widget inputView, Widget indicatorView) { + var padding = _getInputPadding(context); + return Container( + decoration: widget.textareaDecoration ?? + (widget.bordered == true + ? BoxDecoration( + color: widget.decoration != null ? null : (widget.backgroundColor ?? Colors.white), + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault), + border: Border.all(color: TDTheme.of(context).grayColor4), + ) + : null), + padding: widget.bordered == true ? EdgeInsets.all(padding) : null, + child: Column( + children: [ + inputView, + indicatorView, + ], + ), + ); + } + + Widget _getContainer(BuildContext context, Widget labelView, Widget textareaView) { + var padding = _getInputPadding(context); + var isHorizontal = widget.layout == TDTextareaLayout.horizontal; + return Container( + width: widget.width, + decoration: widget.decoration, + padding: widget.padding ?? EdgeInsets.all(padding), + margin: widget.margin, + child: isHorizontal + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + labelView, + Expanded( + child: textareaView, + ), + ], + ) + : Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + labelView, + textareaView, + ], + ), + ); + } + + /// 获取输入框规格 + double _getInputPadding(BuildContext context) { + switch (widget.size) { + case TDInputSize.small: + return TDTheme.of(context).spacer12; + case TDInputSize.large: + return TDTheme.of(context).spacer16; + default: + return TDTheme.of(context).spacer16; + } + } +} diff --git a/tdesign-component/lib/src/components/time_counter/td_time_counter.dart b/tdesign-component/lib/src/components/time_counter/td_time_counter.dart new file mode 100644 index 000000000..196364113 --- /dev/null +++ b/tdesign-component/lib/src/components/time_counter/td_time_counter.dart @@ -0,0 +1,322 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import '../../../tdesign_flutter.dart'; +import '../../util/context_extension.dart'; +import '../../util/list_ext.dart'; + +RegExp _timeReg = RegExp(r'D+|H+|m+|s+|S+'); + +String _toDigits(int n, int l) => n.toString().padLeft(l, '0'); + +String _getMark(String format, String? type) { + var part = format.split(type ?? '')[1]; + if (part.isEmpty) { + return ''; + } + return part.split('')[0]; +} + +/// 计时组件 +class TDTimeCounter extends StatefulWidget { + const TDTimeCounter({ + Key? key, + this.autoStart = true, + this.content = 'default', + this.format = 'HH:mm:ss', + this.millisecond = false, + this.size = TDTimeCounterSize.medium, + this.splitWithUnit = false, + this.theme = TDTimeCounterTheme.defaultTheme, + required this.time, + this.style, + this.onChange, + this.onFinish, + this.direction = TDTimeCounterDirection.down, + this.controller, + }) : super(key: key); + + /// 是否自动开始倒计时 + final bool autoStart; + + /// 'default' / Widget Function(int time) / Widget + final dynamic content; + + /// 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒(分隔符必须为长度为1的非空格的字符) + final String format; + + /// 是否开启毫秒级渲染 + final bool millisecond; + + /// 尺寸 + final TDTimeCounterSize size; + + /// 使用时间单位分割 + final bool splitWithUnit; + + /// 风格 + final TDTimeCounterTheme theme; + + /// 必需;计时时长,单位毫秒 + final int time; + + /// 自定义样式,有则优先用它,没有则根据size和theme选取 + final TDTimeCounterStyle? style; + + /// 时间变化时触发回调 + final Function(int time)? onChange; + + /// 计时结束时触发回调 + final VoidCallback? onFinish; + + /// 计时方向,默认倒计时 + final TDTimeCounterDirection direction; + + /// 控制器,可控制开始/暂停/继续/重置 + final TDTimeCounterController? controller; + + @override + _TDTimeCounterState createState() => _TDTimeCounterState(); +} + +class _TDTimeCounterState extends State with SingleTickerProviderStateMixin { + late TDTimeCounterStyle _style; + late Map timeUnitMap; + Ticker? _ticker; + int _time = 0; + int _tempMilliseconds = 0; + int _maxTime = 0; + + @override + void initState() { + super.initState(); + resetTimer(widget.time, false); + widget.controller?.addListener(_onControllerChanged); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _style = widget.style ?? + TDTimeCounterStyle.generateStyle( + context, + size: widget.size, + theme: widget.theme, + splitWithUnit: widget.splitWithUnit, + ); + timeUnitMap = { + 'D': context.resource.days, + 'H': context.resource.hours, + 'm': context.resource.minutes, + 's': context.resource.seconds, + 'S': context.resource.milliseconds, + }; + } + + @override + void didUpdateWidget(TDTimeCounter oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.controller != oldWidget.controller) { + oldWidget.controller?.removeListener(_onControllerChanged); + widget.controller?.addListener(_onControllerChanged); + } + if (widget.time != oldWidget.time) { + resetTimer(widget.time, false); + } + } + + @override + void dispose() { + _ticker?.dispose(); + widget.controller?.removeListener(_onControllerChanged); + super.dispose(); + } + + /// 开始倒计时 + void startTimer() { + if (_ticker?.isActive == true) { + return; + } + _tempMilliseconds = 0; + _ticker ??= createTicker((Duration elapsed) { + if ((widget.direction == TDTimeCounterDirection.down && _time > 0) || + widget.direction == TDTimeCounterDirection.up && _time < _maxTime) { + setState(() { + if (widget.direction == TDTimeCounterDirection.down) { + _time = max(_time - (elapsed.inMilliseconds - _tempMilliseconds), 0); + } else { + _time = min(_time + (elapsed.inMilliseconds - _tempMilliseconds), _maxTime); + } + }); + _tempMilliseconds = elapsed.inMilliseconds; + widget.onChange?.call(_time); + } else { + pauseTimer(); + widget.onFinish?.call(); + } + setState(() {}); + }); + _ticker!.start(); + } + + /// 暂停 + void pauseTimer() { + _ticker?.stop(); + } + + /// 继续 + void resumeTimer() { + startTimer(); + } + + /// 重置计时 + void resetTimer([int? time, bool update = true]) { + _ticker?.stop(); + if (widget.direction == TDTimeCounterDirection.down) { + _time = time ?? widget.time; + } else { + _time = 0; + _maxTime = time ?? widget.time; + } + if (update) { + setState(() {}); + } + if (widget.autoStart) { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + startTimer(); + }); + } + } + + void _onControllerChanged() { + switch (widget.controller?.value) { + case TDTimeCounterStatus.start: + startTimer(); + break; + case TDTimeCounterStatus.pause: + pauseTimer(); + break; + case TDTimeCounterStatus.resume: + resumeTimer(); + break; + case TDTimeCounterStatus.reset: + resetTimer(widget.controller?.time); + break; + default: + break; + } + } + + @override + Widget build(BuildContext context) { + if (widget.content == 'default') { + return Row(mainAxisSize: MainAxisSize.min, children: _buildTimeWidget(context)); + } + if (widget.content is Function) { + return widget.content(_time); + } + return widget.content; + } + + List _buildTimeWidget(BuildContext context) { + final format = widget.millisecond ? '${widget.format.replaceAll(RegExp(r':S+$'), '')}:SSS' : widget.format; + final matches = _timeReg.allMatches(format); + final timeMap = _getTimeMap(matches.map((e) => e.group(0) ?? '').toList()); + return matches + .map((match) { + final timeType = match.group(0) ?? ''; + return _buildTextWidget( + timeMap[timeType] ?? '0', + widget.splitWithUnit ? timeUnitMap[timeType[0]] ?? '' : _getMark(format, timeType), + ); + }) + .expand((element) => element) + .toList(); + } + + List _buildTextWidget( + String time, + String split, + ) { + final children = [ + Container( + width: _style.timeWidth, + height: _style.timeHeight, + padding: _style.timePadding, + margin: _style.timeMargin, + decoration: _style.timeBox, + child: Center( + child: TDText( + time, + style: TextStyle( + fontFamily: _style.timeFontFamily?.fontFamily, + package: _style.timeFontFamily?.package, + fontSize: _style.timeFontSize, + height: _style.timeFontHeight, + fontWeight: _style.timeFontWeight, + color: _style.timeColor, + ), + ), + ), + ), + ]; + if (split.isNotEmpty) { + children.addAll([ + SizedBox(width: _style.space), + TDText( + split, + style: TextStyle( + fontSize: _style.splitFontSize, + height: _style.splitFontHeight, + fontWeight: _style.splitFontWeight, + color: _style.splitColor, + ), + ), + SizedBox(width: _style.space), + ]); + } + return children; + } + + Map _getTimeMap(List timeType) { + var duration = Duration(milliseconds: _time); + final map = {}; + final dayKey = timeType.find((item) => item.startsWith('D')); + final hourKey = timeType.find((item) => item.startsWith('H')); + final minuteKey = timeType.find((item) => item.startsWith('m')); + final secondKey = timeType.find((item) => item.startsWith('s')); + final millisecondKey = timeType.find((item) => item.startsWith('S')); + if (dayKey != null) { + final length = dayKey.length; + map[dayKey] = _toDigits(duration.inDays, length); + duration = duration - Duration(days: duration.inDays); + } + if (hourKey != null) { + final length = hourKey.length; + final upNum = length > 2 ? pow(10, length).toInt() : 24; + final time = duration.inHours.remainder(upNum); + map[hourKey] = _toDigits(time, length); + duration = duration - Duration(hours: time); + } + if (minuteKey != null) { + final length = minuteKey.length; + final upNum = length > 2 ? pow(10, length).toInt() : 60; + final time = duration.inMinutes.remainder(upNum); + map[minuteKey] = _toDigits(time, length); + duration = duration - Duration(minutes: time); + } + if (secondKey != null) { + final length = secondKey.length; + final upNum = length > 2 ? pow(10, length).toInt() : 60; + final time = duration.inSeconds.remainder(upNum); + map[secondKey] = _toDigits(time, length); + duration = duration - Duration(seconds: time); + } + if (millisecondKey != null) { + final length = millisecondKey.length; + map[millisecondKey] = _toDigits(duration.inMilliseconds, length); + } + return map; + } +} diff --git a/tdesign-component/lib/src/components/time_counter/td_time_counter_controller.dart b/tdesign-component/lib/src/components/time_counter/td_time_counter_controller.dart new file mode 100644 index 000000000..d1744ed46 --- /dev/null +++ b/tdesign-component/lib/src/components/time_counter/td_time_counter_controller.dart @@ -0,0 +1,54 @@ +import 'package:flutter/cupertino.dart'; + +/// 计时组件控制器转态 +enum TDTimeCounterStatus { + /// 开始 + start, + + /// 暂停 + pause, + + /// 继续 + resume, + + /// 重置 + reset, + + /// 空,默认值 + idle, +} + +/// 倒计时组件控制器,可控制开始(`start()`)/暂停(`pause()`)/继续(`resume()`)/重置(`reset([int? time])`) +class TDTimeCounterController extends ValueNotifier { + TDTimeCounterController() : super(TDTimeCounterStatus.idle); + + int? _time; + + int? get time => _time; + + /// 开始 + void start() { + value = TDTimeCounterStatus.start; + } + + /// 暂停 + void pause() { + value = TDTimeCounterStatus.pause; + } + + /// 继续 + void resume() { + value = TDTimeCounterStatus.resume; + } + + /// 重置 + void reset([int? time]) { + if (value == TDTimeCounterStatus.reset) { + _time = time; + notifyListeners(); + } else { + _time = time; + value = TDTimeCounterStatus.reset; + } + } +} diff --git a/tdesign-component/lib/src/components/time_counter/td_time_counter_style.dart b/tdesign-component/lib/src/components/time_counter/td_time_counter_style.dart new file mode 100644 index 000000000..ceaedf08c --- /dev/null +++ b/tdesign-component/lib/src/components/time_counter/td_time_counter_style.dart @@ -0,0 +1,183 @@ +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; + +/// 计时组件计时方向 +enum TDTimeCounterDirection { + /// 倒计时 + down, + /// 正向计时 + up +} + +/// 计时组件尺寸 +enum TDTimeCounterSize { + /// 小 + small, + + /// 中等 + medium, + + /// 大 + large, +} + +/// 计时组件风格 +enum TDTimeCounterTheme { + /// 默认 + defaultTheme, + + /// 圆形 + round, + + /// 方形 + square, +} + +/// 计时组件样式 +class TDTimeCounterStyle { + TDTimeCounterStyle({ + this.timeWidth, + this.timeHeight, + this.timePadding, + this.timeMargin, + this.timeBox, + this.timeFontFamily, + this.timeFontSize, + this.timeFontHeight, + this.timeFontWeight, + this.timeColor, + this.splitFontSize, + this.splitFontHeight, + this.splitFontWeight, + this.splitColor, + this.space, + }); + + /// 时间容器宽度 + double? timeWidth; + + /// 时间容器高度 + double? timeHeight; + + /// 时间容器内边距 + EdgeInsets? timePadding; + + /// 时间容器外边距 + EdgeInsets? timeMargin; + + /// 时间容器装饰 + BoxDecoration? timeBox; + + /// 时间字体 + FontFamily? timeFontFamily; + + /// 时间字体尺寸 + double? timeFontSize; + + /// 时间字体行高 + double? timeFontHeight; + + /// 时间字体粗细 + FontWeight? timeFontWeight; + + /// 时间字体颜色 + Color? timeColor; + + /// 分隔符字体尺寸 + double? splitFontSize; + + /// 分隔符字体行高 + double? splitFontHeight; + + /// 分隔符字体粗细 + FontWeight? splitFontWeight; + + /// 分隔符字体颜色 + Color? splitColor; + + /// 时间与分隔符的间隔 + double? space; + + /// 生成默认样式 + TDTimeCounterStyle.generateStyle( + BuildContext context, { + TDTimeCounterSize? size, + TDTimeCounterTheme? theme, + bool? splitWithUnit, + }) { + timeFontFamily = TDTheme.defaultData().numberFontFamily; + late Font? font; + switch (size ?? TDTimeCounterSize.medium) { + case TDTimeCounterSize.small: + if (theme == TDTimeCounterTheme.defaultTheme) { + timeWidth = timeHeight = null; + font = TDTheme.of(context).fontBodyMedium; + timeFontSize = splitFontSize = font?.size ?? 14; + timeFontHeight = splitFontHeight = font?.height ?? (22 / timeFontSize!); + } else { + timeWidth = timeHeight = 20; + font = TDTheme.of(context).fontBodySmall; + timeFontSize = splitFontSize = font?.size ?? 12; + timeFontHeight = splitFontHeight = null; + } + space = TDTheme.of(context).spacer4 / 2; + break; + case TDTimeCounterSize.medium: + if (theme == TDTimeCounterTheme.defaultTheme) { + timeWidth = timeHeight = null; + font = TDTheme.of(context).fontBodyLarge; + timeFontSize = splitFontSize = font?.size ?? 16; + timeFontHeight = splitFontHeight = font?.height ?? (24 / timeFontSize!); + } else { + timeWidth = timeHeight = 24; + font = TDTheme.of(context).fontBodyMedium; + timeFontSize = splitFontSize = font?.size ?? 14; + timeFontHeight = splitFontHeight = null; + } + space = TDTheme.of(context).spacer8 / 2; + break; + case TDTimeCounterSize.large: + if (theme == TDTimeCounterTheme.defaultTheme) { + timeWidth = timeHeight = null; + font = TDTheme.of(context).fontBodyExtraLarge; + timeFontSize = splitFontSize = font?.size ?? 18; + timeFontHeight = splitFontHeight = font?.height ?? (26 / timeFontSize!); + } else { + timeWidth = timeHeight = 28; + font = TDTheme.of(context).fontBodyLarge; + timeFontSize = splitFontSize = font?.size ?? 16; + timeFontHeight = splitFontHeight = null; + } + space = TDTheme.of(context).spacer12 / 2; + } + + switch (theme ?? TDTimeCounterTheme.defaultTheme) { + case TDTimeCounterTheme.round: + timeBox = BoxDecoration( + shape: BoxShape.circle, + color: TDTheme.of(context).errorColor6, + ); + timeColor = TDTheme.of(context).fontWhColor1; + splitColor = TDTheme.of(context).errorColor6; + break; + case TDTimeCounterTheme.square: + timeBox = BoxDecoration( + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusSmall), + color: TDTheme.of(context).errorColor6, + ); + timeColor = TDTheme.of(context).fontWhColor1; + splitColor = TDTheme.of(context).errorColor6; + break; + case TDTimeCounterTheme.defaultTheme: + timeBox = null; + timeColor = splitColor = TDTheme.of(context).fontGyColor1; + timeWidth = null; + timeHeight = null; + } + + if (splitWithUnit ?? false) { + splitColor = TDTheme.of(context).fontGyColor1; + } + } +} diff --git a/tdesign-component/lib/src/components/toast/td_toast.dart b/tdesign-component/lib/src/components/toast/td_toast.dart new file mode 100644 index 000000000..2832c5a2b --- /dev/null +++ b/tdesign-component/lib/src/components/toast/td_toast.dart @@ -0,0 +1,408 @@ +import 'dart:async'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../../tdesign_flutter.dart'; +import '../../util/auto_size.dart'; +import '../../util/context_extension.dart'; + +enum IconTextDirection { + horizontal, //横向 + vertical //竖向 +} + +class TDToast { + /// 普通文本Toast + static void showText(String? text, + {required BuildContext context, + Duration duration = TDToast._defaultDisPlayDuration, + int? maxLines, + BoxConstraints? constraints, + bool? preventTap, + Widget? customWidget, + Color? backgroundColor}) { + _showOverlay( + _TDTextToast(text: text, maxLines: maxLines, constraints: constraints, customWidget: customWidget,), + context: context, + duration: duration, + preventTap: preventTap, + backgroundColor: backgroundColor); + } + + /// 带图标的Toast + static void showIconText(String? text, + {IconData? icon, + IconTextDirection direction = IconTextDirection.horizontal, + required BuildContext context, + Duration duration = TDToast._defaultDisPlayDuration, + bool? preventTap, + Color? backgroundColor, + int? maxLines}) { + _showOverlay( + _TDIconTextToast( + text: text, + iconData: icon, + iconTextDirection: direction, + maxLines: maxLines, + ), + context: context, + duration: duration, + preventTap: preventTap, + backgroundColor: backgroundColor); + } + + /// 成功提示Toast + static void showSuccess(String? text, + {IconTextDirection direction = IconTextDirection.horizontal, + required BuildContext context, + Duration duration = TDToast._defaultDisPlayDuration, + bool? preventTap, + Color? backgroundColor, + int? maxLines}) { + _showOverlay( + _TDIconTextToast( + text: text, + iconData: TDIcons.check_circle, + iconTextDirection: direction, + maxLines: maxLines, + ), + context: context, + duration: duration, + preventTap: preventTap, + backgroundColor: backgroundColor); + } + + /// 警告Toast + static void showWarning(String? text, + {IconTextDirection direction = IconTextDirection.horizontal, + required BuildContext context, + Duration duration = TDToast._defaultDisPlayDuration, + bool? preventTap, + Color? backgroundColor, + int? maxLines}) { + _showOverlay( + _TDIconTextToast( + text: text, + iconData: TDIcons.error_circle, + iconTextDirection: direction, + maxLines: maxLines, + ), + context: context, + duration: duration, + preventTap: preventTap, + backgroundColor: backgroundColor); + } + + /// 失败提示Toast + static void showFail(String? text, + {IconTextDirection direction = IconTextDirection.horizontal, + required BuildContext context, + Duration duration = TDToast._defaultDisPlayDuration, + bool? preventTap, + Color? backgroundColor, + int? maxLines}) { + _showOverlay( + _TDIconTextToast( + text: text, + iconData: TDIcons.close_circle, + iconTextDirection: direction, + maxLines: maxLines, + ), + context: context, + duration: duration, + preventTap: preventTap, + backgroundColor: backgroundColor); + } + + /// 带文案的加载Toast + static void showLoading( + {required BuildContext context, + String? text, + Duration duration = TDToast._infiniteDuration, + bool? preventTap, + Widget? customWidget, + Color? backgroundColor}) { + _showOverlay( + _TDToastLoading( + text: text, + customWidget: customWidget, + ), + context: context, + duration: duration, + preventTap: preventTap, + backgroundColor: backgroundColor); + } + + /// 不带文案的加载Toast + static void showLoadingWithoutText( + {required BuildContext context, + String? text, + Duration duration = TDToast._infiniteDuration, + bool? preventTap, + Color? backgroundColor}) { + _showOverlay( + const _TDToastLoadingWithoutText(), + context: context, + duration: duration, + preventTap: preventTap, + backgroundColor: backgroundColor); + } + + /// 关闭加载Toast + static void dismissLoading() { + _cancel(); + } + + static void _showOverlay(Widget? widget, + {required BuildContext context, + Duration duration = TDToast._defaultDisPlayDuration, + bool? preventTap, + Color? backgroundColor}) { + _cancel(); + _showing = true; + var overlayState = Overlay.of(context); + _overlayEntry = OverlayEntry( + builder: (BuildContext context) => Center( + child: AnimatedOpacity( + opacity: _showing ? 1.0 : 0.0, + duration: _showing ? const Duration(milliseconds: 100) : const Duration(milliseconds: 200), + child: widget, + ), + )); + + if(preventTap ?? false) { + _overlayEntry = OverlayEntry( + builder: (BuildContext context) => Positioned( + top: 0, + right: 0, + bottom: 0, + left: 0, + child: Container( + color: backgroundColor, + child: Align( + alignment: Alignment.center, + child: AnimatedOpacity( + opacity: _showing ? 1.0 : 0.0, + duration: _showing ? const Duration(milliseconds: 100) : const Duration(milliseconds: 200), + child: widget, + ), + ), + ), + ), + ); + } + if (_overlayEntry != null) { + overlayState?.insert(_overlayEntry!); + } + _startTimer(duration); + } + + static void _cancel() { + _timer?.cancel(); + _timer = null; + _disposeTimer?.cancel(); + _disposeTimer = null; + _overlayEntry?.remove(); + _overlayEntry = null; + _showing = false; + } + + static void _startTimer(Duration duration) { + _timer?.cancel(); + _disposeTimer?.cancel(); + _timer = Timer(duration, () { + _showing = false; + _overlayEntry?.markNeedsBuild(); + _timer = null; + _disposeTimer = Timer(const Duration(milliseconds: 200), () { + _overlayEntry?.remove(); + _overlayEntry = null; + _disposeTimer = null; + }); + }); + } + + static OverlayEntry? _overlayEntry; + static bool _showing = false; + static Timer? _timer; + static Timer? _disposeTimer; + static const Duration _defaultDisPlayDuration = Duration(milliseconds: 3000); + static const Duration _infiniteDuration = Duration(seconds: 99999999); +} + +class _TDIconTextToast extends StatelessWidget { + final String? text; + final IconData? iconData; + final IconTextDirection iconTextDirection; + final int? maxLines; + + const _TDIconTextToast({this.text, this.iconData, this.iconTextDirection = IconTextDirection.horizontal, this.maxLines}); + + Widget buildHorizontalWidgets(BuildContext context) { + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 191, maxHeight: 94), + child: Container( + padding: const EdgeInsets.fromLTRB(24, 14, 24, 14), + decoration: BoxDecoration( + color: TDTheme.of(context).fontGyColor1, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + iconData, + size: 24, + color: TDTheme.of(context).whiteColor1, + ), + const SizedBox( + width: 8, + ), + TDText( + text ?? '', + font: TDTheme.of(context).fontBodyMedium, + fontWeight: FontWeight.w400, + maxLines: 1, + overflow: TextOverflow.ellipsis, + textColor: TDTheme.of(context).whiteColor1, + ) + ], + )), + ); + } + + Widget buildVerticalWidgets(BuildContext context) { + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 136), + child: Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: TDTheme.of(context).fontGyColor1, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + iconData, + size: 32, + color: TDTheme.of(context).whiteColor1, + ), + const SizedBox( + height: 8, + ), + TDText( + text ?? '', + font: TDTheme.of(context).fontBodyMedium, + fontWeight: FontWeight.w400, + maxLines: maxLines ?? 1, + overflow: TextOverflow.ellipsis, + textColor: TDTheme.of(context).whiteColor1, + ) + ], + ))); + } + + @override + Widget build(BuildContext context) { + return iconTextDirection == IconTextDirection.horizontal + ? buildHorizontalWidgets(context) + : buildVerticalWidgets(context); + } +} + +class _TDToastLoading extends StatelessWidget { + final String? text; + + final Widget? customWidget; + + const _TDToastLoading({this.text, this.customWidget}); + + @override + Widget build(BuildContext context) { + return Container( + height: 110, + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: TDTheme.of(context).fontGyColor1, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + TDCircleIndicator( + color: TDTheme.of(context).whiteColor1, + size: 26, + lineWidth: 4, + ), + const SizedBox( + height: 8, + ), + customWidget ?? TDText( + text ?? context.resource.loadingWithPoint, + font: TDTheme.of(context).fontBodyMedium, + fontWeight: FontWeight.w400, + maxLines: 1, + overflow: TextOverflow.ellipsis, + textColor: TDTheme.of(context).whiteColor1, + ) + ], + )); + } +} + +class _TDToastLoadingWithoutText extends StatelessWidget { + const _TDToastLoadingWithoutText(); + + @override + Widget build(BuildContext context) { + return Container( + width: 80, + height: 80, + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + color: TDTheme.of(context).fontGyColor1, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault), + ), + child: TDCircleIndicator( + color: TDTheme.of(context).whiteColor1, + size: 26, + lineWidth: 4, + )); + } +} + +class _TDTextToast extends StatelessWidget { + final String? text; + + final int? maxLines; + + final BoxConstraints? constraints; + + final Widget? customWidget; + + const _TDTextToast({this.text, this.maxLines, this.constraints, this.customWidget}); + + @override + Widget build(BuildContext context) { + return ConstrainedBox( + constraints: constraints ?? BoxConstraints(maxWidth: 191.scale), + child: Container( + padding: const EdgeInsets.fromLTRB(24, 16, 24, 16), + decoration: BoxDecoration( + color: TDTheme.of(context).fontGyColor1, + borderRadius: BorderRadius.circular(TDTheme.of(context).radiusDefault), + ), + child: customWidget ?? TDText( + text ?? '', + font: TDTheme.of(context).fontBodyMedium, + fontWeight: FontWeight.w400, + maxLines: maxLines ?? 3, + overflow: TextOverflow.ellipsis, + textColor: TDTheme.of(context).whiteColor1, + )), + ); + } +} diff --git a/tdesign-component/lib/src/components/tree/td_tree_select.dart b/tdesign-component/lib/src/components/tree/td_tree_select.dart new file mode 100644 index 000000000..6f27dbb44 --- /dev/null +++ b/tdesign-component/lib/src/components/tree/td_tree_select.dart @@ -0,0 +1,316 @@ +import 'package:flutter/material.dart'; + +import '../../../tdesign_flutter.dart'; + +typedef TDTreeSelectChangeEvent = void Function(List, int level); + +class TDSelectOption { + TDSelectOption( + {required this.label, required this.value, this.children = const [], this.multiple = false}); + + /// 标签 + final String label; + + /// 值 + final int value; + + /// 子选项 + List children; + + /// 当前子项支持多选 + final bool multiple; +} + +enum TDTreeSelectStyle { + normal, + outline, +} + +class TDTreeSelect extends StatefulWidget { + const TDTreeSelect( + {Key? key, + this.options = const [], + this.defaultValue = const [], + this.onChange, + this.multiple = false, + this.style = TDTreeSelectStyle.normal, + this.height = 336}) + : super(key: key); + + /// 展示的选项列表 + final List options; + + /// 初始值,对应options中的value值 + final List defaultValue; + + /// 选中值发生变化 + final TDTreeSelectChangeEvent? onChange; + + /// 高度 + final double height; + + /// 支持多选 + final bool multiple; + + /// 一级菜单样式 + final TDTreeSelectStyle style; + + @override + State createState() => _TDTreeSelectState(); +} + +class _TDTreeSelectState extends State { + ScrollController controller2 = ScrollController(); + ScrollController controller3 = ScrollController(); + + List values = []; + int get currentLevel => values.length + 1; + int? get firstValue => values.isNotEmpty ? values[0] : null; + dynamic get secondValue => values.length >= 2 ? values[1] : null; + dynamic get thirdValue => values.length >= 3 ? values[2] : null; + + List get firstOptions => widget.options; + List get secondOptions => maxLevel() <= 1 || values.isEmpty + ? [] + : firstOptions + .firstWhere((opt) => opt.value == firstValue, + orElse: () => TDSelectOption(value: -1, label: '', children: [])) + .children; + List get thirdOptions => maxLevel() <= 2 || currentLevel < 3 + ? [] + : secondOptions + .firstWhere((opt) => opt.value == secondValue, + orElse: () => TDSelectOption(value: -1, label: '', children: [])) + .children; + + @override + void initState() { + super.initState(); + + values = List.from(widget.defaultValue); + if (values.isEmpty && widget.options.isNotEmpty) { + final option = widget.options[0]; + values.add((widget.multiple || option.multiple) ? [option.value] : option.value); + } + } + + int maxLevel() { + if (widget.options.isEmpty) { + return 1; + } + + var secondLevelOptions = widget.options + .where((element) => element.children.isNotEmpty) + .map((ele) => ele.children) + .toList(); + if (secondLevelOptions.isEmpty) { + return 1; + } + + var hasThirdLevel = secondLevelOptions + .any((list) => list.any((element) => element.children.isNotEmpty)); + + return hasThirdLevel ? 3 : 2; + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + SizedBox( + width: 106, + child: TDSideBar( + height: widget.height, + value: firstValue, + style: widget.style == TDTreeSelectStyle.outline + ? TDSideBarStyle.outline + : TDSideBarStyle.normal, + children: widget.options + .map((ele) => TDSideBarItem( + value: ele.value, + label: ele.label, + )) + .toList(), + onSelected: (value) { + setState(() { + if (values.isEmpty) { + values.add(value); + } else { + values = [value]; + if (controller2.hasClients) { + controller2.jumpTo(0); + } + } + + if (widget.onChange != null) { + widget.onChange!(values, 1); + } + }); + }, + ), + ), + Expanded( + child: Container( + height: widget.height, + decoration: const BoxDecoration(color: Colors.white), + child: _buildRightParts(context), + )) + ], + ); + } + + Widget _buildRightParts(BuildContext context) { + return Visibility( + visible: maxLevel() >= 2, + child: maxLevel() == 2 + ? Container( + child: _buildNextColumn(context, level: 2), + ) + : Row( + children: [ + SizedBox( + width: 103, + child: + _buildNextColumn(context, level: 2, lastColumn: false), + ), + Expanded(child: _buildNextColumn(context, level: 3)) + ], + )); + } + + Widget _buildNextColumn(BuildContext context, + {int level = 2, bool lastColumn = true}) { + var displayOptions = level == 2 ? secondOptions : thirdOptions; + return MediaQuery.removePadding( + context: context, + removeTop: true, + removeBottom: true, + child: ListView.builder( + controller: level == 2 ? controller2 : controller3, + itemExtent: 56, + itemCount: displayOptions.length, + itemBuilder: (BuildContext ctx, int index) { + var currentValue = displayOptions[index].value; + final isMultiple = widget.multiple ? widget.multiple : displayOptions[index].multiple; + // 判断是否被选中 + var selected = false; + if (isMultiple) { + if (level == 2) { + if (maxLevel() == 2) { + selected = secondValue != null + ? (secondValue as List).contains(currentValue) + : false; + } else { + selected = secondValue == currentValue; + } + } else { + selected = thirdValue != null + ? (thirdValue as List).contains(currentValue) + : false; + } + } else { + selected = + (level == 2 ? secondValue : thirdValue) == currentValue; + } + + return GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + setState(() { + if (level == 2) { + switch (values.length) { + case 1: + values.add(isMultiple + ? [currentValue] + : currentValue); + break; + case 2: + if (isMultiple) { + var hasContains = (values[1] as List) + .contains(currentValue); + if (hasContains) { + (values[1] as List).remove(currentValue); + } else { + (values[1] as List).add(currentValue); + } + } else { + values[1] = currentValue; + } + if (controller3.hasClients) { + controller3.jumpTo(0); + } + break; + default: + values[1] = currentValue; + values.removeLast(); + if (controller3.hasClients) { + controller3.jumpTo(0); + } + } + } else { + switch (values.length) { + case 1: + case 2: + values.add(isMultiple + ? [currentValue] + : currentValue); + break; + default: + if (isMultiple) { + var hasContains = (values[2] as List) + .contains(currentValue); + if (hasContains) { + (values[2] as List).remove(currentValue); + } else { + (values[2] as List).add(currentValue); + } + } else { + values[2] = currentValue; + } + } + } + + if (widget.onChange != null) { + widget.onChange!(values, level); + } + }); + }, + child: SizedBox( + height: 56, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only( + top: 16, left: 16, bottom: 16), + child: TDText( + displayOptions[index].label, + textColor: (!lastColumn && selected) + ? TDTheme.of(context).brandNormalColor + : const Color.fromRGBO(0, 0, 0, 0.9), + style: TextStyle( + fontSize: 16, + fontWeight: (!lastColumn && selected) + ? FontWeight.w600 + : FontWeight.w400), + ), + ), + Visibility( + visible: lastColumn && selected, + child: SizedBox( + width: 56, + height: 56, + child: Padding( + padding: const EdgeInsets.all(16), + child: Icon( + TDIcons.check, + color: TDTheme.of(context).brandNormalColor, + ), + ), + )) + ], + ), + )); + })); + } +} diff --git a/tdesign-component/lib/src/components/upload/td_upload.dart b/tdesign-component/lib/src/components/upload/td_upload.dart new file mode 100644 index 000000000..179fc10e7 --- /dev/null +++ b/tdesign-component/lib/src/components/upload/td_upload.dart @@ -0,0 +1,465 @@ +import 'dart:io'; +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:image_picker/image_picker.dart'; + +import '../../../tdesign_flutter.dart'; + +enum TDUploadMediaType { + image, // 图片 + video, // 视频 +} + +enum TDUploadValidatorError { + overSize, // 超出文件大小 + overQuantity, // 超出文件数量限制 +} + +enum TDUploadFileStatus { + success, // 成功 + loading, // 加载中 + error, // 失败 + retry, // 重试 +} + +enum TDUploadType { + add, // 添加 + remove, // 删除 + replace, // 替换 +} + +enum TDUploadBoxType { + roundedSquare, // 圆角方形 + circle, // 圆形 +} + +class TDUploadFile { + TDUploadFile( + {required this.key, + this.remotePath, + this.assetPath, + this.file, + this.progress, + this.status = TDUploadFileStatus.success, + this.loadingText = 'Loading...', + this.retryText = 'Re-Upload', + this.errorText = 'Error', + this.canDelete = true}); + + final int key; + final String? remotePath; + final String? assetPath; + final File? file; + final bool canDelete; + final int? progress; + final String loadingText; + final String retryText; + final String errorText; + TDUploadFileStatus status; +} + +typedef TDUploadErrorEvent = void Function(Object e); +typedef TDUploadClickEvent = void Function(int value); +typedef TDUploadValueChangedEvent = void Function(List files, TDUploadType type); +typedef TDUploadValidatorEvent = void Function(TDUploadValidatorError e); + +class TDUpload extends StatefulWidget { + const TDUpload( + {Key? key, + this.max = 0, + this.mediaType = const [TDUploadMediaType.image, TDUploadMediaType.video], + this.sizeLimit, + this.onCancel, + this.onError, + this.onValidate, + this.onClick, + this.onMaxLimitReached, + required this.files, + this.onChange, + this.multiple = false, + this.width = 80.0, + this.height = 80.0, + this.type = TDUploadBoxType.roundedSquare, this.disabled=false, + this.enabledReplaceType = false}) + : super(key: key); + + /// 控制展示的文件列表 + final List files; + + /// 用于控制文件上传数量,0为不限制,仅在multiple为true时有效 + final int max; + + /// 支持上传的文件类型,图片或视频 + final List mediaType; + + /// 图片大小限制,单位为KB + final double? sizeLimit; + + /// 是否多选上传,默认false + final bool multiple; + + /// 监听取消上传 + final VoidCallback? onCancel; + + /// 监听获取资源错误 + final TDUploadErrorEvent? onError; + + /// 监听文件校验出错 + final TDUploadValidatorEvent? onValidate; + + /// 监听点击图片位 + final TDUploadClickEvent? onClick; + + /// 监听文件超过最大数量 + final VoidCallback? onMaxLimitReached; + + /// 监听添加, 删除和替换media事件 + final TDUploadValueChangedEvent? onChange; + + /// 图片宽度 + final double? width; + + /// 图片高度 + final double? height; + + /// Box类型 + final TDUploadBoxType type; + + /// 是否启用replace功能 + final bool? enabledReplaceType; + + ///是否禁用 + final bool? disabled; + @override + State createState() => _TDUploadState(); +} + +class _TDUploadState extends State { + List fileList = []; + + bool get canUpload => widget.multiple ? (widget.max == 0 ? true : fileList.length < widget.max) : fileList.isEmpty; + final ImagePicker _picker = ImagePicker(); + + // 类型映射 + final Map _imageTypeMap = { + TDUploadBoxType.roundedSquare: TDImageType.roundedSquare, + TDUploadBoxType.circle: TDImageType.circle, + }; + + @override + initState() { + super.initState(); + fileList = widget.files; + WidgetsBinding.instance.addPostFrameCallback((_) { + _validateInitialFiles(); + }); + } + + void _validateInitialFiles() { + if (widget.max > 0 && fileList.length > widget.max) { + if (widget.onMaxLimitReached != null) { + widget.onMaxLimitReached!(); + } else if (widget.onValidate != null) { + widget.onValidate!(TDUploadValidatorError.overQuantity); + } else { + throw Exception("Initial file count exceeds the maximum limit"); + } + } + } + + // 获取相册照片或视频 + Future> getMediaFromPicker(bool isMultiple) async { + if (widget.mediaType.isEmpty) { + return []; + } + + var medias = []; + try { + if (isMultiple) { + medias = await _picker.pickMultiImage(); + } else { + XFile? media; + if (widget.mediaType.contains(TDUploadMediaType.image)) { + media = await _picker.pickImage(source: ImageSource.gallery); + } else { + media = await _picker.pickVideo(source: ImageSource.gallery); + } + if (media != null) { + medias = [media]; + } + } + + if (widget.max > 0 && fileList.length + medias.length > widget.max) { + if (widget.onMaxLimitReached != null) { + widget.onMaxLimitReached!(); + } else if (widget.onValidate != null) { + widget.onValidate!(TDUploadValidatorError.overQuantity); + } + return []; + } + } on PlatformException catch (e) { + if (widget.onError != null) { + widget.onError!(e); + } + } catch (e) { + if (widget.onError != null) { + widget.onError!(e); + } + } + + return medias; + } + + // 处理获取到的资源 + void extractImageList(List files) async { + if (!canUpload || files.isEmpty) { + return; + } + + var result = await validateResources(files); + + if (result != null) { + if (widget.onValidate != null) { + widget.onValidate!(result); + } + return; + } + + var originMaxKeys = fileList.isEmpty ? 0 : fileList.map((file) => file.key).reduce(max); + + var newFiles = []; + for (var i = 0; i < files.length; i++) { + newFiles.add(TDUploadFile(key: originMaxKeys + i + 1, file: File(files[i].path), assetPath: files[i].path)); + } + + if (widget.onChange != null) { + widget.onChange!(newFiles, TDUploadType.add); + } + } + + // 替换资源 + void replaceMedia(List files, TDUploadFile oldFile) async { + if (files.isEmpty || files.length != 1) { + return; + } + + var result = await validateResources(files); + + if (result != null) { + if (widget.onValidate != null) { + widget.onValidate!(result); + } + return; + } + + var newFile = TDUploadFile(key: oldFile.key, file: File(files[0].path), assetPath: files[0].path); + + if (widget.onChange != null) { + widget.onChange!([newFile], TDUploadType.replace); + } + } + + // 校验资源 + Future validateResources(List files, [bool? multiple]) async { + TDUploadValidatorError? error; + + // 多选逻辑,优选从参数获取 + var isMultiple = widget.multiple; + if (multiple != null) { + isMultiple = multiple; + } + + if (isMultiple && widget.max > 0) { + var remain = widget.max - fileList.length; + + if (files.length > remain) { + error = TDUploadValidatorError.overQuantity; + return error; + } + } + + for (var file in files) { + if (widget.sizeLimit != null) { + final fileSize = await file.length(); + final sizeLimitInBytes = widget.sizeLimit! * 1024; + if (fileSize > sizeLimitInBytes) { + error = TDUploadValidatorError.overSize; + break; + } + } + } + + return error; + } + + // 删除资源 + void onDelete(TDUploadFile file) { + if (widget.onChange != null) { + widget.onChange!([file], TDUploadType.remove); + } + } + + @override + Widget build(BuildContext context) { + return SizedBox( + width: double.infinity, + child: Wrap( + spacing: 8, + runSpacing: 16, + children: [ + ...fileList.map((file) => _buildImageBox(context, file)).toList(), + _buildUploadBox(context, shouldDisplay: canUpload, onTap: () async { + if (!canUpload||widget.disabled!) { + return; + } + + final files = await getMediaFromPicker(widget.multiple); + extractImageList(files); + }), + ], + ), + ); + } + + Widget _buildUploadBox(BuildContext context, {void Function()? onTap, bool shouldDisplay = true}) { + return Visibility( + visible: shouldDisplay, + child: GestureDetector( + onTap: onTap, + child: Container( + width: widget.width, + height: widget.height, + decoration: widget.type == TDUploadBoxType.circle + ? BoxDecoration( + shape: BoxShape.circle, + color: TDTheme.of(context).grayColor1, + ) + : BoxDecoration(color: TDTheme.of(context).grayColor1, borderRadius: BorderRadius.circular(6)), + child: const Center( + child: Icon( + TDIcons.add, + color: Color.fromRGBO(0, 0, 0, 0.4), + size: 28, + )), + ))); + } + + Widget _buildImageBox(BuildContext context, TDUploadFile file) { + return GestureDetector( + onTap: () async { + if (widget.onClick != null) { + widget.onClick!(file.key); + } + // 替换资源 + if (widget.enabledReplaceType ?? false) { + final files = await getMediaFromPicker(false); + replaceMedia(files, file); + } + }, + child: Stack( + children: [ + TDImage( + key: Key(file.assetPath ?? ''), + width: widget.width, + height: widget.height, + imgUrl: file.remotePath, + // assetUrl: file.assetPath, + imageFile: file.file, + type: _imageTypeMap[widget.type] ?? TDImageType.roundedSquare, + ), + Visibility(visible: file.status != TDUploadFileStatus.success, child: _buildShadowBox(file)), + Visibility( + visible: file.canDelete, + child: Positioned( + right: 0, + top: 0, + child: GestureDetector( + onTap: () { + onDelete(file); + }, + child: Container( + width: 20, + height: 20, + decoration: widget.type == TDUploadBoxType.circle + ? const BoxDecoration( + shape: BoxShape.circle, + color: Color.fromRGBO(0, 0, 0, 0.6), + ) + : const BoxDecoration( + color: Color.fromRGBO(0, 0, 0, 0.6), + borderRadius: + BorderRadius.only(bottomLeft: Radius.circular(6), topRight: Radius.circular(6))), + child: const Center( + child: Icon( + TDIcons.close, + size: 16, + color: Colors.white, + )), + ), + ))) + ], + ), + ); + } + + Widget _buildShadowBox(TDUploadFile file) { + var displayText = ''; + switch (file.status) { + case TDUploadFileStatus.loading: + displayText = file.progress != null ? '${file.progress!}%' : file.loadingText; + break; + case TDUploadFileStatus.retry: + displayText = file.retryText; + break; + case TDUploadFileStatus.error: + displayText = file.errorText; + break; + default: + } + + return Container( + width: widget.width, + height: widget.height, + decoration: widget.type == TDUploadBoxType.circle + ? const BoxDecoration( + shape: BoxShape.circle, + color: Color.fromRGBO(0, 0, 0, 0.4), + ) + : BoxDecoration(color: const Color.fromRGBO(0, 0, 0, 0.4), borderRadius: BorderRadius.circular(6)), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Visibility( + visible: file.status == TDUploadFileStatus.loading, + child: const TDLoading( + size: TDLoadingSize.large, + icon: TDLoadingIcon.circle, + iconColor: Colors.white, + ), + ), + Visibility( + visible: file.status == TDUploadFileStatus.retry || file.status == TDUploadFileStatus.error, + child: Icon( + file.status == TDUploadFileStatus.retry ? TDIcons.refresh : TDIcons.close_circle, + size: 24, + color: Colors.white, + )), + Padding( + padding: const EdgeInsets.only(top: 4), + child: TDText( + displayText, + textColor: Colors.white, + style: const TextStyle(fontSize: 12, height: 1.67), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/tdesign-component/lib/src/theme/basic.dart b/tdesign-component/lib/src/theme/basic.dart new file mode 100644 index 000000000..243e18500 --- /dev/null +++ b/tdesign-component/lib/src/theme/basic.dart @@ -0,0 +1,41 @@ +import 'package:flutter/cupertino.dart'; + +/// 字体宽高数据 +class Font { + late double size; + late double height; + late FontWeight fontWeight; + + Font({required int size, required int lineHeight, this.fontWeight = FontWeight.w400}) { + this.size = size.toDouble(); + height = lineHeight.toDouble() / size; + } + + factory Font.fromJson(Map map) => + Font(size: map['size'], lineHeight: map['lineHeight'], fontWeight: _getFontWeight(map)); + + static FontWeight _getFontWeight(Map map) { + int weight = map['fontWeight'] ?? 4; + return FontWeight.values[weight - 1]; + } +} + +/// 字体样式 +class FontFamily { + late String fontFamily; + String? package; + + FontFamily({required this.fontFamily, this.package}); + + factory FontFamily.fromJson(Map map) => + FontFamily(fontFamily: map['fontFamily'], package: map['package']); +} + +/// Font字体宽高的扩展 +extension FontExtensions on Font { + Font withSize(int newSize) => Font( + size: newSize, + lineHeight: (height * newSize).round(), + fontWeight: fontWeight + ); +} diff --git a/tdesign-component/lib/src/theme/resource_delegate.dart b/tdesign-component/lib/src/theme/resource_delegate.dart new file mode 100644 index 000000000..018820f37 --- /dev/null +++ b/tdesign-component/lib/src/theme/resource_delegate.dart @@ -0,0 +1,355 @@ +import 'package:flutter/cupertino.dart'; + +import '../../tdesign_flutter.dart'; + +typedef TDTDResourceBuilder = TDResourceDelegate? Function(BuildContext context); + +/// 资源管理器 +class TDResourceManager { + /// 代理构建器 + TDTDResourceBuilder? _builder; + + /// 每次都调用build方法 + bool _needAlwaysBuild = false; + + TDResourceDelegate? _delegate; + + /// 获取资源 + TDResourceDelegate delegate(BuildContext context) { + if (_builder == null) { + return _defaultDelegate; + } + if (_needAlwaysBuild) { + // 每次都调用,适用于全局有多个TDResourceDelegate的情况 + var delegate = _builder?.call(context); + if (delegate != null) { + return delegate; + } + } + _delegate ??= _builder?.call(context); + return _delegate ?? _defaultDelegate; + } + + static TDResourceManager? _instance; + + /// 单例对象 + static TDResourceManager get instance { + _instance ??= TDResourceManager(); + return _instance!; + } + + /// 获取资源 + static final _defaultDelegate = _DefaultResourceDelegate(); + + /// 设置资源代理 + void setResourceBuilder(TDTDResourceBuilder delegate, needAlwaysBuild) { + _builder = delegate; + _needAlwaysBuild = needAlwaysBuild; + } +} + +/// 资源管理器,允许外部重写,设计成抽象类,防止有新增字段时,用户没有感知 +abstract class TDResourceDelegate { + /// [TDSwitch]的打开状态文案 + String get open; + + /// [TDSwitch]的关闭状态文案 + String get close; + + /// [TDBadge]为0时的默认文案 + String get badgeZero; + + /// [TDAlertDialog]等 取消 + String get cancel; + + /// [TDAlertDialog]等 确认 + String get confirm; + + /// [TDDropdownMenu] 其他 + String get other; + + /// [TDDropdownMenu] 重置 + String get reset; + + /// [TDLoading] 加载中 + String get loading; + + /// [TDToast] 加载中... + String get loadingWithPoint; + + /// [TDConfirmDialog] 知道了 + String get knew; + + /// [TDRefreshHeader] 正在刷新 + String get refreshing; + + /// [TDRefreshHeader] 松开刷新 + String get releaseRefresh; + + /// [TDRefreshHeader] 下拉刷新 + String get pullToRefresh; + + /// [TDRefreshHeader] 刷新完成 + String get completeRefresh; + + /// [TDTimeCounter] 天 + String get days; + + /// [TDTimeCounter] 时 + String get hours; + + /// [TDTimeCounter] 分 + String get minutes; + + /// [TDTimeCounter] 秒 + String get seconds; + + /// [TDTimeCounter] 毫秒 + String get milliseconds; + + /// [TDDatePicker] 年 + String get yearLabel; + + /// [TDDatePicker] 月 + String get monthLabel; + + /// [TDDatePicker] 日 + String get dateLabel; + + /// [TDDatePicker] 周 + String get weeksLabel; + + /// [TDCalendarHeader] 星期日 + String get sunday; + + /// [TDCalendarHeader] 星期一 + String get monday; + + /// [TDCalendarHeader] 星期二 + String get tuesday; + + /// [TDCalendarHeader] 星期三 + String get wednesday; + + /// [TDCalendarHeader] 星期四 + String get thursday; + + /// [TDCalendarHeader] 星期五 + String get friday; + + /// [TDCalendarHeader] 星期六 + String get saturday; + + /// [TDCalendarBody] 年 + String get year; + + /// [TDCalendarBody] 一月 + String get january; + + /// [TDCalendarBody] 二月 + String get february; + + /// [TDCalendarBody] 三月 + String get march; + + /// [TDCalendarBody] 四月 + String get april; + + /// [TDCalendarBody] 五月 + String get may; + + /// [TDCalendarBody] 六月 + String get june; + + /// [TDCalendarBody] 七月 + String get july; + + /// [TDCalendarBody] 八月 + String get august; + + /// [TDCalendarBody] 九月 + String get september; + + /// [TDCalendarBody] 十月 + String get october; + + /// [TDCalendarBody] 十一月 + String get november; + + /// [TDCalendarBody] 十二月 + String get december; + + /// [TDCalendar] 时间 + String get time; + + /// [TDCalendar] 开始 + String get start; + + /// [TDCalendar] 结束 + String get end; + + /// [TDRate] 未评分 + String get notRated; + + /// [TDRate] 选择选项 + String get cascadeLabel; + + /// [TDBackTop] 返回 + String get back; + + /// [TDBackTop] 顶部 + String get top; + +} + +/// 如果用户要重写,就应该全部重写,不开放只重新部分资源 +class _DefaultResourceDelegate extends TDResourceDelegate { + @override + String get open => '开'; + + @override + String get close => '关'; + + @override + String get badgeZero => '0'; + + @override + String get cancel => '取消'; + + @override + String get confirm => '确定'; + + @override + String get other => '其它'; + + @override + String get reset => '重置'; + + @override + String get loading => '加载中'; + + @override + String get loadingWithPoint => '加载中...'; + + @override + String get knew => '知道了'; + + @override + String get refreshing => '正在刷新'; + + @override + String get releaseRefresh => '松开刷新'; + + @override + String get pullToRefresh => '下拉刷新'; + + @override + String get completeRefresh => '刷新完成'; + + @override + String get days => '天'; + + @override + String get hours => '时'; + + @override + String get minutes => '分'; + + @override + String get seconds => '秒'; + + @override + String get milliseconds => '毫秒'; + + @override + String get yearLabel => '年'; + + @override + String get monthLabel => '月'; + + @override + String get dateLabel=>'日'; + + @override + String get weeksLabel=>'周'; + + String get sunday => '日'; + + @override + String get monday => '一'; + + @override + String get tuesday => '二'; + + @override + String get wednesday => '三'; + + @override + String get thursday => '四'; + + @override + String get friday => '五'; + + @override + String get saturday => '六'; + + @override + String get year => ' 年'; + + @override + String get january => '1 月'; + + @override + String get february => '2 月'; + + @override + String get march => '3 月'; + + @override + String get april => '4 月'; + + @override + String get may => '5 月'; + + @override + String get june => '6 月'; + + @override + String get july => '7 月'; + + @override + String get august => '8 月'; + + @override + String get september => '9 月'; + + @override + String get october => '10 月'; + + @override + String get november => '11 月'; + + @override + String get december => '12 月'; + + @override + String get time => '时间'; + + @override + String get start => '开始'; + + @override + String get end => '结束'; + + @override + String get notRated => '未评分'; + + @override + String get cascadeLabel => '选择选项'; + + @override + String get back => '返回'; + + @override + String get top => '顶部'; +} diff --git a/lib/src/theme/td_colors.dart b/tdesign-component/lib/src/theme/td_colors.dart similarity index 83% rename from lib/src/theme/td_colors.dart rename to tdesign-component/lib/src/theme/td_colors.dart index 526e30d20..2cfccd8f6 100644 --- a/lib/src/theme/td_colors.dart +++ b/tdesign-component/lib/src/theme/td_colors.dart @@ -14,206 +14,206 @@ import 'td_theme.dart'; extension TDColors on TDThemeData { /// 功能色组---------------------------------------------------- - ///#ECF2FE - Color get brandColor1 => colorMap['brandColor1'] ?? const Color(0xFFECF2FE); + ///#F2F3FF + Color get brandColor1 => colorMap['brandColor1'] ?? const Color(0xFFF2F3FF); - ///#D4E3FC - Color get brandColor2 => colorMap['brandColor2'] ?? const Color(0xFFD4E3FC); + ///#D9E1FF + Color get brandColor2 => colorMap['brandColor2'] ?? const Color(0xFFD9E1FF); - ///#BBD3FB - Color get brandColor3 => colorMap['brandColor3'] ?? const Color(0xFFBBD3FB); + ///#B5C7FF + Color get brandColor3 => colorMap['brandColor3'] ?? const Color(0xFFB5C7FF); - ///#96BBF8 - Color get brandColor4 => colorMap['brandColor4'] ?? const Color(0xFF96BBF8); + ///#8EABFF + Color get brandColor4 => colorMap['brandColor4'] ?? const Color(0xFF8EABFF); - ///#699EF5 - Color get brandColor5 => colorMap['brandColor5'] ?? const Color(0xFF699EF5); + ///#618DFF + Color get brandColor5 => colorMap['brandColor5'] ?? const Color(0xFF618DFF); - ///#4787F0 - Color get brandColor6 => colorMap['brandColor6'] ?? const Color(0xFF4787F0); - - ///#266FE8 - Color get brandColor7 => colorMap['brandColor7'] ?? const Color(0xFF266FE8); + ///#366EF4 + Color get brandColor6 => colorMap['brandColor6'] ?? const Color(0xFF366EF4); ///#0052D9 - Color get brandColor8 => colorMap['brandColor8'] ?? const Color(0xFF0052D9); + Color get brandColor7 => colorMap['brandColor7'] ?? const Color(0xFF0052D9); + + ///#003CAB + Color get brandColor8 => colorMap['brandColor8'] ?? const Color(0xFF003CAB); - ///#0034B5 - Color get brandColor9 => colorMap['brandColor9'] ?? const Color(0xFF0034B5); + ///#002A7C + Color get brandColor9 => colorMap['brandColor9'] ?? const Color(0xFF002A7C); - ///#001F97 - Color get brandColor10 => colorMap['brandColor10'] ?? const Color(0xFF001F97); + ///#001A57 + Color get brandColor10 => colorMap['brandColor10'] ?? const Color(0xFF001A57); - ///#ECF2FE + ///#F2F3FF Color get brandLightColor => colorMap['brandLightColor'] ?? brandColor1; - ///#D4E3FC + ///#D9E1FF Color get brandFocusColor => colorMap['brandFocusColor'] ?? brandColor2; - ///#BBD3FB + ///#B5C7FF Color get brandDisabledColor => colorMap['brandDisabledColor'] ?? brandColor3; - ///#266FE8 - Color get brandHoverColor => colorMap['brandHoverColor'] ?? brandColor7; + ///#366EF4 + Color get brandHoverColor => colorMap['brandHoverColor'] ?? brandColor6; ///#0052D9 - Color get brandNormalColor => colorMap['brandNormalColor'] ?? brandColor8; + Color get brandNormalColor => colorMap['brandNormalColor'] ?? brandColor7; - ///#0034B5 - Color get brandClickColor => colorMap['brandClickColor'] ?? brandColor9; + ///#003CAB + Color get brandClickColor => colorMap['brandClickColor'] ?? brandColor8; /// 错误色组---------------------------------------------------- - ///#FDECEE - Color get errorColor1 => colorMap['errorColor1'] ?? const Color(0xFFFDECEE); + ///#FFF0ED + Color get errorColor1 => colorMap['errorColor1'] ?? const Color(0xFFFFF0ED); - ///#F9D7D9 - Color get errorColor2 => colorMap['errorColor2'] ?? const Color(0xFFF9D7D9); + ///#FFD8D2 + Color get errorColor2 => colorMap['errorColor2'] ?? const Color(0xFFFFD8D2); - ///#F8B9BE - Color get errorColor3 => colorMap['errorColor3'] ?? const Color(0xFFF8B9BE); + ///#FFB9B0 + Color get errorColor3 => colorMap['errorColor3'] ?? const Color(0xFFFFB9B0); - ///#F78D94 - Color get errorColor4 => colorMap['errorColor4'] ?? const Color(0xFFF78D94); + ///#FF9285 + Color get errorColor4 => colorMap['errorColor4'] ?? const Color(0xFFFF9285); - ///#F36D78 - Color get errorColor5 => colorMap['errorColor5'] ?? const Color(0xFFF36D78); + ///#F6685D + Color get errorColor5 => colorMap['errorColor5'] ?? const Color(0xFFF6685D); - ///#E34D59 - Color get errorColor6 => colorMap['errorColor6'] ?? const Color(0xFFE34D59); + ///#D54941 + Color get errorColor6 => colorMap['errorColor6'] ?? const Color(0xFFD54941); - ///#C9353F - Color get errorColor7 => colorMap['errorColor7'] ?? const Color(0xFFC9353F); + ///#AD352F + Color get errorColor7 => colorMap['errorColor7'] ?? const Color(0xFFAD352F); - ///#B11F26 - Color get errorColor8 => colorMap['errorColor8'] ?? const Color(0xFFB11F26); + ///#881F1C + Color get errorColor8 => colorMap['errorColor8'] ?? const Color(0xFF881F1C); - ///#951114 - Color get errorColor9 => colorMap['errorColor9'] ?? const Color(0xFF951114); + ///#68070A + Color get errorColor9 => colorMap['errorColor9'] ?? const Color(0xFF68070A); - ///#680506 - Color get errorColor10 => colorMap['errorColor10'] ?? const Color(0xFF680506); + ///#490002 + Color get errorColor10 => colorMap['errorColor10'] ?? const Color(0xFF490002); - ///#FDECEE + ///#FFF0ED Color get errorLightColor => colorMap['errorLightColor'] ?? errorColor1; - ///#F9D7D9 + ///#FFD8D2 Color get errorFocusColor => colorMap['errorFocusColor'] ?? errorColor2; - ///#F8B9BE + ///#FFB9B0 Color get errorDisabledColor => colorMap['errorDisabledColor'] ?? errorColor3; - ///#F36D78 + ///#F6685D Color get errorHoverColor => colorMap['errorHoverColor'] ?? errorColor5; - ///#E34D59 + ///#D54941 Color get errorNormalColor => colorMap['errorNormalColor'] ?? errorColor6; - ///#C9353F + ///#AD352F Color get errorClickColor => colorMap['errorClickColor'] ?? errorColor7; /// 警告色组---------------------------------------------------- - ///#FEF3E6 - Color get warningColor1 => colorMap['warningColor1'] ?? const Color(0xFFFEF3E6); + ///#FFF1E9 + Color get warningColor1 => colorMap['warningColor1'] ?? const Color(0xFFFFF1E9); - ///#F9E0C7 - Color get warningColor2 => colorMap['warningColor2'] ?? const Color(0xFFF9E0C7); + ///#FFD9C2 + Color get warningColor2 => colorMap['warningColor2'] ?? const Color(0xFFFFD9C2); - ///#F7C797 - Color get warningColor3 => colorMap['warningColor3'] ?? const Color(0xFFF7C797); + ///#FFB98C + Color get warningColor3 => colorMap['warningColor3'] ?? const Color(0xFFFFB98C); - ///#F2995F - Color get warningColor4 => colorMap['warningColor4'] ?? const Color(0xFFF2995F); + ///#FA9550 + Color get warningColor4 => colorMap['warningColor4'] ?? const Color(0xFFFA9550); - ///#ED7B2F - Color get warningColor5 => colorMap['warningColor5'] ?? const Color(0xFFED7B2F); + ///#E37318 + Color get warningColor5 => colorMap['warningColor5'] ?? const Color(0xFFE37318); - ///#D35A21 - Color get warningColor6 => colorMap['warningColor6'] ?? const Color(0xFFD35A21); + ///#BE5A00 + Color get warningColor6 => colorMap['warningColor6'] ?? const Color(0xFFBE5A00); - ///#BA431B - Color get warningColor7 => colorMap['warningColor7'] ?? const Color(0xFFBA431B); + ///#954500 + Color get warningColor7 => colorMap['warningColor7'] ?? const Color(0xFF954500); - ///#9E3610 - Color get warningColor8 => colorMap['warningColor8'] ?? const Color(0xFF9E3610); + ///#713300 + Color get warningColor8 => colorMap['warningColor8'] ?? const Color(0xFF713300); - ///#842B0B - Color get warningColor9 => colorMap['warningColor9'] ?? const Color(0xFF842B0B); + ///#532300 + Color get warningColor9 => colorMap['warningColor9'] ?? const Color(0xFF532300); - ///#5A1907 + ///#3B1700 Color get warningColor10 => - colorMap['warningColor10'] ?? const Color(0xFF5A1907); + colorMap['warningColor10'] ?? const Color(0xFF3B1700); - ///#FEF3E6 + ///#FFF1E9 Color get warningLightColor => colorMap['warningLightColor'] ?? warningColor1; - ///#F9E0C7 + ///#FFD9C2 Color get warningFocusColor => colorMap['warningFocusColor'] ?? warningColor2; - ///#F7C797 + ///#FFB98C Color get warningDisabledColor => colorMap['warningDisabledColor'] ?? warningColor3; - ///#F2995F + ///#FA9550 Color get warningHoverColor => colorMap['warningHoverColor'] ?? warningColor4; - ///#ED7B2F + ///#E37318 Color get warningNormalColor => colorMap['warningNormalColor'] ?? warningColor5; - ///#D35A21 + ///#BE5A00 Color get warningClickColor => colorMap['warningClickColor'] ?? warningColor6; /// 成功色组---------------------------------------------------- - ///#E8F8F2 - Color get successColor1 => colorMap['successColor1'] ?? const Color(0xFFE8F8F2); + ///#E3F9E9 + Color get successColor1 => colorMap['successColor1'] ?? const Color(0xFFE3F9E9); - ///#BCEBDC - Color get successColor2 => colorMap['successColor2'] ?? const Color(0xFFBCEBDC); + ///#C6F3D7 + Color get successColor2 => colorMap['successColor2'] ?? const Color(0xFFC6F3D7); - ///#85DBBE - Color get successColor3 => colorMap['successColor3'] ?? const Color(0xFF85DBBE); + ///#92DAB2 + Color get successColor3 => colorMap['successColor3'] ?? const Color(0xFF92DAB2); - ///#48C79C - Color get successColor4 => colorMap['successColor4'] ?? const Color(0xFF48C79C); + ///#56C08D + Color get successColor4 => colorMap['successColor4'] ?? const Color(0xFF56C08D); - ///#00A870 - Color get successColor5 => colorMap['successColor5'] ?? const Color(0xFF00A870); + ///#2BA471 + Color get successColor5 => colorMap['successColor5'] ?? const Color(0xFF2BA471); - ///#078D5C - Color get successColor6 => colorMap['successColor6'] ?? const Color(0xFF078D5C); + ///#008858 + Color get successColor6 => colorMap['successColor6'] ?? const Color(0xFF008858); - ///#067945 - Color get successColor7 => colorMap['successColor7'] ?? const Color(0xFF067945); + ///#006C45 + Color get successColor7 => colorMap['successColor7'] ?? const Color(0xFF006C45); - ///#056334 - Color get successColor8 => colorMap['successColor8'] ?? const Color(0xFF056334); + ///#005334 + Color get successColor8 => colorMap['successColor8'] ?? const Color(0xFF005334); - ///#044F2A - Color get successColor9 => colorMap['successColor9'] ?? const Color(0xFF044F2A); + ///#003B23 + Color get successColor9 => colorMap['successColor9'] ?? const Color(0xFF003B23); - ///#033017 + ///#002515 Color get successColor10 => - colorMap['successColor10'] ?? const Color(0xFF033017); + colorMap['successColor10'] ?? const Color(0xFF002515); - ///#E8F8F2 + ///#E3F9E9 Color get successLightColor => colorMap['successLightColor'] ?? successColor1; - ///#BCEBDC + ///#C6F3D7 Color get successFocusColor => colorMap['successFocusColor'] ?? successColor2; - ///#85DBBE + ///#92DAB2 Color get successDisabledColor => colorMap['successDisabledColor'] ?? successColor3; - ///#48C79C + ///#56C08D Color get successHoverColor => colorMap['successHoverColor'] ?? successColor4; - ///#00A870 + ///#2BA471 Color get successNormalColor => colorMap['successNormalColor'] ?? successColor5; - ///#078D5C + ///#008858 Color get successClickColor => colorMap['successClickColor'] ?? successColor6; /// 文字色组---------------------------------------------------- diff --git a/tdesign-component/lib/src/theme/td_default_theme.dart b/tdesign-component/lib/src/theme/td_default_theme.dart new file mode 100644 index 000000000..24ab3bd73 --- /dev/null +++ b/tdesign-component/lib/src/theme/td_default_theme.dart @@ -0,0 +1,315 @@ +/// TDesign默认主题 +class TDDefaultTheme { + static String defaultThemeConfig = ''' + { + "default": { + "ref":{ + "brandLightColor": "brandColor1", + "brandFocusColor": "brandColor2", + "brandDisabledColor": "brandColor3", + "brandHoverColor": "brandColor6", + "brandNormalColor": "brandColor7", + "brandClickColor": "brandColor8", + "errorLightColor": "errorColor1", + "errorFocusColor": "errorColor2", + "errorDisabledColor": "errorColor3", + "errorHoverColor": "errorColor5", + "errorNormalColor": "errorColor6", + "errorClickColor": "errorColor7", + "warningLightColor": "warningColor1", + "warningFocusColor": "warningColor2", + "warningDisabledColor": "warningColor3", + "warningHoverColor": "warningColor4", + "warningNormalColor": "warningColor5", + "warningClickColor": "warningColor6", + "successLightColor": "successColor1", + "successFocusColor": "successColor2", + "successDisabledColor": "successColor3", + "successHoverColor": "successColor4", + "successNormalColor": "successColor5", + "successClickColor": "successColor6" + }, + "color": { + "brandColor1": "#F2F3FF", + "brandColor2": "#D9E1FF", + "brandColor3": "#B5C7FF", + "brandColor4": "#8EABFF", + "brandColor5": "#618DFF", + "brandColor6": "#366EF4", + "brandColor7": "#0052D9", + "brandColor8": "#003CAB", + "brandColor9": "#002A7C", + "brandColor10": "#001A57", + "errorColor1": "#FFF0ED", + "errorColor2": "#FFD8D2", + "errorColor3": "#FFB9B0", + "errorColor4": "#FF9285", + "errorColor5": "#F6685D", + "errorColor6": "#D54941", + "errorColor7": "#AD352F", + "errorColor8": "#881F1C", + "errorColor9": "#68070A", + "errorColor10": "#490002", + "warningColor1": "#FFF1E9", + "warningColor2": "#FFD9C2", + "warningColor3": "#FFB98C", + "warningColor4": "#FA9550", + "warningColor5": "#E37318", + "warningColor6": "#BE5A00", + "warningColor7": "#954500", + "warningColor8": "#713300", + "warningColor9": "#532300", + "warningColor10": "#3B1700", + "successColor1": "#E3F9E9", + "successColor2": "#C6F3D7", + "successColor3": "#92DAB2", + "successColor4": "#56C08D", + "successColor5": "#2BA471", + "successColor6": "#008858", + "successColor7": "#006C45", + "successColor8": "#005334", + "successColor9": "#003B23", + "successColor10": "#002515", + "fontGyColor1": "#E6000000", + "fontGyColor2": "#99000000", + "fontGyColor3": "#66000000", + "fontGyColor4": "#42000000", + "fontWhColor1": "#FFFFFFFF", + "fontWhColor2": "#8CFFFFFF", + "fontWhColor3": "#59FFFFFF", + "fontWhColor4": "#38FFFFFF", + "whiteColor1": "#FFFFFF", + "grayColor1": "#F3F3F3", + "grayColor2": "#EEEEEE", + "grayColor3": "#E7E7E7", + "grayColor4": "#DCDCDC", + "grayColor5": "#C5C5C5", + "grayColor6": "#A6A6A6", + "grayColor7": "#8B8B8B", + "grayColor8": "#777777", + "grayColor9": "#5E5E5E", + "grayColor10": "#4B4B4B", + "grayColor11": "#383838", + "grayColor12": "#2C2C2C", + "grayColor13": "#242424", + "grayColor14": "#181818" + }, + "font": { + "fontDisplayLarge": { + "size": 64, + "lineHeight": 72, + "fontWeight": 6 + }, + "fontDisplayMedium": { + "size": 48, + "lineHeight": 56, + "fontWeight": 6 + }, + "fontHeadlineLarge": { + "size": 36, + "lineHeight": 44, + "fontWeight": 6 + }, + "fontHeadlineMedium": { + "size": 28, + "lineHeight": 36, + "fontWeight": 6 + }, + "fontHeadlineSmall": { + "size": 24, + "lineHeight": 32, + "fontWeight": 6 + }, + "fontTitleExtraLarge": { + "size": 20, + "lineHeight": 28, + "fontWeight": 6 + }, + "fontTitleLarge": { + "size": 18, + "lineHeight": 26, + "fontWeight": 6 + }, + "fontTitleMedium": { + "size": 16, + "lineHeight": 24, + "fontWeight": 6 + }, + "fontTitleSmall": { + "size": 14, + "lineHeight": 22 + }, + "fontBodyExtraLarge": { + "size": 18, + "lineHeight": 26 + }, + "fontBodyLarge": { + "size": 16, + "lineHeight": 24 + }, + "fontBodyMedium": { + "size": 14, + "lineHeight": 22 + }, + "fontBodySmall": { + "size": 12, + "lineHeight": 20 + }, + "fontBodyExtraSmall": { + "size": 10, + "lineHeight": 16 + }, + "fontMarkLarge": { + "size": 16, + "lineHeight": 24, + "fontWeight": 6 + }, + "fontMarkMedium": { + "size": 14, + "lineHeight": 22, + "fontWeight": 6 + }, + "fontMarkSmall": { + "size": 12, + "lineHeight": 20, + "fontWeight": 6 + }, + "fontMarkExtraSmall": { + "size": 10, + "lineHeight": 16, + "fontWeight": 6 + }, + "fontLinkLarge": { + "size": 16, + "lineHeight": 24 + }, + "fontLinkMedium": { + "size": 14, + "lineHeight": 22 + }, + "fontLinkSmall": { + "size": 12, + "lineHeight": 20 + } + }, + "fontFamily": { + "numberFontFamily": { + "fontFamily": "TCloudNumber", + "package": "tdesign_flutter" + } + }, + "radius": { + "radiusSmall": 3, + "radiusDefault": 6, + "radiusLarge": 9, + "radiusExtraLarge": 12, + "radiusRound": 9999, + "radiusCircle": 9999 + }, + "shadow": { + "shadowsBase": [ + { + "color": "#0D000000", + "blurRadius": 10, + "spreadRadius": 1, + "offset": { + "x": 0, + "y": 1 + } + }, + { + "color": "#14000000", + "blurRadius": 5, + "spreadRadius": 1, + "offset": { + "x": 0, + "y": 4 + } + }, + { + "color": "#1F000000", + "blurRadius": 4, + "spreadRadius": -1, + "offset": { + "x": 0, + "y": 2 + } + } + ], + "shadowsMiddle": [ + { + "color": "#0D000000", + "blurRadius": 14, + "spreadRadius": 2, + "offset": { + "x": 0, + "y": 3 + } + }, + { + "color": "#0F000000", + "blurRadius": 10, + "spreadRadius": 1, + "offset": { + "x": 0, + "y": 8 + } + }, + { + "color": "#1A000000", + "blurRadius": 5, + "spreadRadius": -3, + "offset": { + "x": 0, + "y": 0 + } + } + ], + "shadowsTop": [ + { + "color": "#0D000000", + "blurRadius": 30, + "spreadRadius": 5, + "offset": { + "x": 0, + "y": 6 + } + }, + { + "color": "#0A000000", + "blurRadius": 24, + "spreadRadius": 2, + "offset": { + "x": 0, + "y": 16 + } + }, + { + "color": "#14000000", + "blurRadius": 10, + "spreadRadius": -5, + "offset": { + "x": 0, + "y": 8 + } + } + ] + }, + "spacer": { + "spacer4": 4, + "spacer8": 8, + "spacer12": 12, + "spacer16": 16, + "spacer24": 24, + "spacer32": 32, + "spacer40": 40, + "spacer48": 48, + "spacer64": 64, + "spacer96": 96, + "spacer160": 160 + } + } +} + + '''; +} diff --git a/lib/src/theme/td_font_family.dart b/tdesign-component/lib/src/theme/td_font_family.dart similarity index 100% rename from lib/src/theme/td_font_family.dart rename to tdesign-component/lib/src/theme/td_font_family.dart diff --git a/tdesign-component/lib/src/theme/td_fonts.dart b/tdesign-component/lib/src/theme/td_fonts.dart new file mode 100644 index 000000000..b57db5a9b --- /dev/null +++ b/tdesign-component/lib/src/theme/td_fonts.dart @@ -0,0 +1,71 @@ +import 'basic.dart'; +import 'td_theme.dart'; + +/// 内置字体数据 +extension TDFonts on TDThemeData { + /// 字体大小/行高 + /// 64/72 + Font? get fontDisplayLarge => fontMap['fontDisplayLarge']; + + /// 48/56 + Font? get fontDisplayMedium => fontMap['fontDisplayMedium']; + + /// 36/44 + Font? get fontHeadlineLarge => fontMap['fontHeadlineLarge']; + + /// 28/36 + Font? get fontHeadlineMedium => fontMap['fontHeadlineMedium']; + + /// 24/32 + Font? get fontHeadlineSmall => fontMap['fontHeadlineSmall']; + + /// 20/28 + Font? get fontTitleExtraLarge => fontMap['fontTitleExtraLarge']; + + /// 18/26 + Font? get fontTitleLarge => fontMap['fontTitleLarge']; + + /// 16/24 + Font? get fontTitleMedium => fontMap['fontTitleMedium']; + + /// 14/22 + Font? get fontTitleSmall => fontMap['fontTitleSmall']; + + /// 18/26 + Font? get fontBodyExtraLarge => fontMap['fontBodyExtraLarge']; + + /// 16/24 + Font? get fontBodyLarge => fontMap['fontBodyLarge']; + + /// 14/22 + Font? get fontBodyMedium => fontMap['fontBodyMedium']; + + /// 12/20 + Font? get fontBodySmall => fontMap['fontBodySmall']; + + /// 10/16 + Font? get fontBodyExtraSmall => fontMap['fontBodyExtraSmall']; + + /// 16/24 + Font? get fontMarkLarge => fontMap['fontMarkLarge']; + + /// 14/22 + Font? get fontMarkMedium => fontMap['fontMarkMedium']; + + /// 12/20 + Font? get fontMarkSmall => fontMap['fontMarkSmall']; + + /// 10/16 + Font? get fontMarkExtraSmall => fontMap['fontMarkExtraSmall']; + + /// 16/24 + Font? get fontLinkLarge => fontMap['fontLinkLarge']; + + /// 14/22 + Font? get fontLinkMedium => fontMap['fontLinkMedium']; + + /// 12/20 + Font? get fontLinkSmall => fontMap['fontLinkSmall']; + + +} diff --git a/tdesign-component/lib/src/theme/td_radius.dart b/tdesign-component/lib/src/theme/td_radius.dart new file mode 100644 index 000000000..2fc7a89cb --- /dev/null +++ b/tdesign-component/lib/src/theme/td_radius.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +import 'td_theme.dart'; + +/// 内置圆角数据 +extension TDRadius on TDThemeData { + /// 圆角数据 + double get radiusSmall => radiusMap['radiusSmall'] ?? 3; + double get radiusDefault => radiusMap['radiusDefault'] ?? 6; + double get radiusLarge => radiusMap['radiusLarge'] ?? 9; + double get radiusExtraLarge => radiusMap['radiusExtraLarge'] ?? 12; + + /// 胶囊型,数值设置较大 + double get radiusRound => radiusMap['radiusRound'] ?? 9999; + + /// 圆形与胶囊型一致,如果长款一致即是圆形 + double get radiusCircle => radiusMap['radiusCircle'] ?? 9999; +} diff --git a/tdesign-component/lib/src/theme/td_shadows.dart b/tdesign-component/lib/src/theme/td_shadows.dart new file mode 100644 index 000000000..586933645 --- /dev/null +++ b/tdesign-component/lib/src/theme/td_shadows.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; +import 'td_theme.dart'; + +/// 内置投影 +extension TDBoxShadows on TDThemeData { + /// 基础投影 + List? get shadowsBase => shadowMap['shadowsBase']; + + /// 中层投影 + List? get shadowsMiddle => shadowMap['shadowsMiddle']; + + /// 上层投影 + List? get shadowsTop => shadowMap['shadowsTop']; +} diff --git a/lib/src/theme/td_spacers.dart b/tdesign-component/lib/src/theme/td_spacers.dart similarity index 100% rename from lib/src/theme/td_spacers.dart rename to tdesign-component/lib/src/theme/td_spacers.dart diff --git a/tdesign-component/lib/src/theme/td_theme.dart b/tdesign-component/lib/src/theme/td_theme.dart new file mode 100644 index 000000000..9747088b9 --- /dev/null +++ b/tdesign-component/lib/src/theme/td_theme.dart @@ -0,0 +1,380 @@ +import 'dart:convert'; + +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; + +import '../../tdesign_flutter.dart'; +import '../util/log.dart'; +import '../util/string_util.dart'; +import 'td_default_theme.dart'; + +/// 主题控件 +class TDTheme extends StatelessWidget { + + const TDTheme( + {required this.data, required this.child, this.systemData, Key? key}) + : super(key: key); + + /// 仅使用Default主题,不需要切换主题功能 + static bool _needMultiTheme = false; + + /// 主题数据 + static TDThemeData? _singleData; + + /// 子控件 + final Widget child; + + /// 主题数据 + final TDThemeData data; + + /// Flutter系统主题数据 + final ThemeData? systemData; + + @override + Widget build(BuildContext context) { + + if(!_needMultiTheme){ + _singleData = data; + } + var extensions = [data]; + return Theme(data: systemData?.copyWith( + extensions: extensions + ) ?? ThemeData(extensions: extensions), child: child); + } + + /// 开启多套主题功能 + static void needMultiTheme([bool value = true]) { + _needMultiTheme = value; + } + + /// 设置资源代理, + /// needAlwaysBuild=true:每次都会走build方法;如果全局有多个Delegate,需要区分情况去获取,则可以设置needAlwaysBuild为true,业务自己判断返回哪个delegate + /// needAlwaysBuild=false:返回delegate为null,则每次都会走build方法,返回了 + static void setResourceBuilder(TDTDResourceBuilder delegate,{bool needAlwaysBuild = false}){ + TDResourceManager.instance.setResourceBuilder(delegate,needAlwaysBuild); + } + + /// 获取默认主题数据,全局唯一 + static TDThemeData defaultData() { + return TDThemeData.defaultData(); + } + + /// 获取主题数据,如果未传context则获取全局唯一的默认数据, + /// 传了context,则获取最近的主题,取不到则会获取全局唯一默认数据 + static TDThemeData of([BuildContext? context]) { + if(!_needMultiTheme || context == null){ + // 如果context为null,则返回全局默认主题 + return _singleData ?? TDThemeData.defaultData(); + } + // 如果传了context,则从其中获取最近主题 + try { + var data = Theme.of(context).extensions[TDThemeData] as TDThemeData?; + return data ?? TDThemeData.defaultData(); + } catch (e) { + Log.w('TDTheme', 'TDTheme.of() error: $e'); + return TDThemeData.defaultData(); + } + } + + /// 获取主题数据,取不到则可空 + /// 传了context,则获取最近的主题,取不到或未传context则返回null, + static TDThemeData? ofNullable([BuildContext? context]) { + if (context != null) { + // 如果传了context,则从其中获取最近主题 + return Theme.of(context).extensions[TDThemeData] as TDThemeData?; + } else { + // 如果context为null,则返回null + return null; + } + } +} + +/// 主题数据 +class TDThemeData extends ThemeExtension { + static const String _defaultThemeName = 'default'; + static TDThemeData? _defaultThemeData; + + /// 名称 + late String name; + /// 颜色 + late TDMap colorMap; + /// 字体尺寸 + late TDMap fontMap; + /// 圆角 + late TDMap radiusMap; + /// 字体样式 + late TDMap fontFamilyMap; + /// 阴影 + late TDMap> shadowMap; + /// 间隔 + late TDMap spacerMap; + /// 映射关系 + late TDMap refMap; + /// 额外定义的结构 + late TDExtraThemeData? extraThemeData; + + TDThemeData( + {required this.name, + required this.colorMap, + required this.fontMap, + required this.radiusMap, + required this.fontFamilyMap, + required this.shadowMap, + required this.spacerMap, + required this.refMap, + this.extraThemeData,}); + + /// 获取默认Data,一个App里只有一个,用于没有context的地方 + static TDThemeData defaultData({TDExtraThemeData? extraThemeData}) { + _defaultThemeData ??= fromJson( + _defaultThemeName, TDDefaultTheme.defaultThemeConfig, + extraThemeData: extraThemeData) ?? + _emptyData(_defaultThemeName, extraThemeData: extraThemeData); + + return _defaultThemeData!; + } + + /// 从父类拷贝 + TDThemeData copyWithTDThemeData( + String name, { + Map? colorMap, + Map? fontMap, + Map? radiusMap, + Map? fontFamilyMap, + Map>? shadowMap, + Map? marginMap, + TDExtraThemeData? extraThemeData, + }) { + + return copyWith(name: name,colorMap: colorMap,fontMap: fontMap,radiusMap: radiusMap,fontFamilyMap: fontFamilyMap,shadowMap: shadowMap,marginMap: marginMap,extraThemeData: extraThemeData) as TDThemeData; + } + + @override + ThemeExtension copyWith({ + String? name, + Map? colorMap, + Map? fontMap, + Map? radiusMap, + Map? fontFamilyMap, + Map>? shadowMap, + Map? marginMap, + TDExtraThemeData? extraThemeData, + }) { + var result = TDThemeData( + name: name ?? 'default', + colorMap: _copyMap(this.colorMap, colorMap), + fontMap: _copyMap(this.fontMap, fontMap), + radiusMap: _copyMap(this.radiusMap, radiusMap), + fontFamilyMap: _copyMap(this.fontFamilyMap, fontFamilyMap), + shadowMap: _copyMap>(this.shadowMap, shadowMap), + spacerMap: _copyMap(spacerMap, marginMap), + refMap: _copyMap(refMap, refMap), + extraThemeData: extraThemeData ?? this.extraThemeData); + return result; + } + + /// 拷贝Map,防止内层 + TDMap _copyMap(TDMap src, Map? add) { + var map = TDMap(factory: ()=>src); + + src.forEach((key, value) { + map[key] = value; + }); + if (add != null) { + map.addAll(add); + } + return map; + } + + /// 创建空对象 + static TDThemeData _emptyData(String name, + {TDExtraThemeData? extraThemeData}) { + var refMap = TDMap(); + return TDThemeData( + name: name, + colorMap: TDMap(factory: () => defaultData().colorMap, refs: refMap), + fontMap: TDMap(factory: () => defaultData().fontMap, refs:refMap), + radiusMap: TDMap(factory: () => defaultData().radiusMap, refs: refMap), + fontFamilyMap: TDMap(factory: () => defaultData().fontFamilyMap, refs:refMap), + shadowMap: TDMap(factory: () => defaultData().shadowMap, refs: refMap), + spacerMap: TDMap(factory: () => defaultData().spacerMap, refs: refMap), + refMap: refMap); + } + + /// 解析配置的json文件为主题数据 + static TDThemeData? fromJson(String name, String themeJson, + {var recoverDefault = false, TDExtraThemeData? extraThemeData}) { + if (themeJson.isEmpty) { + Log.e('TTheme', 'parse themeJson is empty'); + return null; + } + try { + /// 要求json配置必须正确 + final themeConfig = json.decode(themeJson); + if (themeConfig.containsKey(name)) { + var theme = _emptyData(name); + Map curThemeMap = themeConfig['$name']; + + /// 设置颜色 + Map? colorsMap = curThemeMap['color']; + colorsMap?.forEach((key, value) { + var color = toColor(value); + if (color != null) { + theme.colorMap[key] = color; + } + }); + + /// 设置颜色 + Map? refMap = curThemeMap['ref']; + refMap?.forEach((key, value) { + theme.refMap[key] = value; + }); + + /// 设置字体尺寸 + Map? fontsMap = curThemeMap['font']; + fontsMap?.forEach((key, value) { + theme.fontMap[key] = + Font.fromJson(value); + }); + + /// 设置圆角 + Map? cornersMap = curThemeMap['radius']; + cornersMap?.forEach((key, value) { + theme.radiusMap[key] = value.toDouble(); + }); + + /// 设置字体 + Map? fontFamilyMap = curThemeMap['fontFamily']; + fontFamilyMap?.forEach((key, value) { + theme.fontFamilyMap[key] = FontFamily.fromJson(value); + }); + + /// 设置阴影 + Map? shadowMap = curThemeMap['shadow']; + shadowMap?.forEach((key, value) { + var list = []; + (value as List).forEach((element) { + list.add(BoxShadow( + color: toColor(element['color']) ?? Colors.black, + blurRadius: element['blurRadius'].toDouble(), + spreadRadius: element['spreadRadius'].toDouble(), + offset: Offset(element['offset']?['x'].toDouble() ?? 0, + element['offset']?['y'].toDouble() ?? 0), + )); + }); + + theme.shadowMap[key] = list; + }); + + /// 设置Margin + Map? marginsMap = curThemeMap['margin']; + marginsMap?.forEach((key, value) { + theme.spacerMap[key] = value.toDouble(); + }); + + if (extraThemeData != null) { + extraThemeData.parse(name, curThemeMap); + theme.extraThemeData = extraThemeData; + } + if (recoverDefault) { + _defaultThemeData = theme; + } + return theme; + } else { + Log.e('TTheme', + 'load theme error ,not found the theme with name:${name}'); + return null; + } + } catch (e) { + Log.e('TTheme', 'parse theme data error:${e}'); + return null; + } + } + + Color? ofColor( + String? key, + ) { + return colorMap[key]; + } + + Font? ofFont(String? key) { + return fontMap[key]; + } + + double? ofCorner( + String? key, + ) { + return radiusMap[key]; + } + + FontFamily? ofFontFamily( + String? key, + ) { + return fontFamilyMap[key]; + } + + List? ofShadow( + String? key, + ) { + return shadowMap[key]; + } + + T? ofExtra() { + try { + return extraThemeData as T; + } catch (e) { + Log.e('TDThemeData ofExtra error: $e'); + } + return null; + } + + @override + ThemeExtension lerp(ThemeExtension? other, double t) { + if (other is! TDThemeData) { + return this; + } + return TDThemeData( + name: other.name, + colorMap: other.colorMap, + fontMap: other.fontMap, + radiusMap: other.radiusMap, + fontFamilyMap: other.fontFamilyMap, + shadowMap: other.shadowMap, + spacerMap: other.spacerMap, + refMap: other.refMap); + } +} + +/// 扩展主题数据 +abstract class TDExtraThemeData { + /// 解析json + void parse(String name, Map curThemeMap); +} + +typedef DefaultMapFactory = TDMap? Function(); + +/// 自定义Map +class TDMap extends DelegatingMap{ + TDMap({this.factory, this.refs}) : super({}); + DefaultMapFactory? factory; + TDMap? refs; + + @override + V? operator [](Object? key) { + // return super[key]; + key = refs?[key] ?? key; + var value = super[key]; + if(value != null){ + return value; + } + var defaultValue = factory?.call()?.get(key); + if(defaultValue is V){ + return defaultValue; + } + return null; + } + + V? get(Object? key){ + return super[key]; + } +} \ No newline at end of file diff --git a/lib/src/util/auto_size.dart b/tdesign-component/lib/src/util/auto_size.dart similarity index 100% rename from lib/src/util/auto_size.dart rename to tdesign-component/lib/src/util/auto_size.dart diff --git a/tdesign-component/lib/src/util/context_extension.dart b/tdesign-component/lib/src/util/context_extension.dart new file mode 100644 index 000000000..3f79822c4 --- /dev/null +++ b/tdesign-component/lib/src/util/context_extension.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +import '../theme/resource_delegate.dart'; + +/// Context的扩展,方便使用 +extension ContextExtension on BuildContext { + TDResourceDelegate get resource => TDResourceManager.instance.delegate(this); +} \ No newline at end of file diff --git a/lib/src/util/iterable_ext.dart b/tdesign-component/lib/src/util/iterable_ext.dart similarity index 89% rename from lib/src/util/iterable_ext.dart rename to tdesign-component/lib/src/util/iterable_ext.dart index 97c0ce3c8..fca681e62 100644 --- a/lib/src/util/iterable_ext.dart +++ b/tdesign-component/lib/src/util/iterable_ext.dart @@ -82,5 +82,15 @@ extension IterableExt on Iterable { return firstWhereOrNull((element) => test(element)) != null; } + /// + /// 获取指定索引的元素,如果索引越界,则返回null + /// + T? getOrNull(int index) { + if (index < 0 || index >= length) { + return null; + } + return elementAt(index); + } + } \ No newline at end of file diff --git a/tdesign-component/lib/src/util/list_ext.dart b/tdesign-component/lib/src/util/list_ext.dart new file mode 100644 index 000000000..045849687 --- /dev/null +++ b/tdesign-component/lib/src/util/list_ext.dart @@ -0,0 +1,73 @@ +/// 扩展 [List] 类 +extension ListExt on List { + /// 将当前列表分割成大小为 [size] 的子列表。 + /// + /// 如果原始列表的长度不能被 [size] 整除,则最后一个子列表可能会包含少于 [size] 个元素。 + /// + /// 示例: + /// + /// ```dart + /// List numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + /// List> chunks = numbers.chunk(3); + /// print(chunks); // 输出:[[1, 2, 3], [4, 5, 6], [7, 8, 9]] + /// ``` + List> chunk(int size) { + return [for (int i = 0; i < length; i += size) sublist(i, i + size > length ? length : i + size)]; + } + + /// 根据 [keySelector] 函数从列表中的每个元素中提取键,并将元素按键分组。 + /// + /// 返回一个 [Map],其中键是 [keySelector] 函数返回的键,值是包含具有相同键的所有元素的列表。 + /// + /// 示例: + /// + /// ```dart + /// List numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]; + /// Map> evenOdd = numbers.groupBy((number) => number % 2); + /// print(evenOdd); // 输出:{0: [2, 4, 6, 8], 1: [1, 3, 5, 7, 9]} + /// ``` + Map> groupBy(E Function(T) keySelector) { + var groupedItems = >{}; + + for (var item in this) { + var key = keySelector(item); + if (groupedItems[key] == null) { + groupedItems[key] = []; + } + groupedItems[key]!.add(item); + } + + return groupedItems; + } + + /// 在列表中查找满足指定条件的第一个元素,可选地限制在指定的索引范围内。 + /// + /// 此方法遍历列表,从索引`startIndex`开始至`endIndex`(包含)结束, + /// 应用`test`函数于每个元素。当`test`函数对某个元素返回`true`时,该元素被返回。 + /// 如果没有元素满足条件,则返回`null`。 + /// + /// 参数: + /// - [test]: 用于测试列表元素是否满足条件的函数。 + /// - [startIndex]: 查找的起始索引,默认为0。 + /// - [endIndex]: 查找的结束索引,默认为列表的长度减一,意味着默认遍历整个列表。 + /// + /// 示例: + /// + /// ```dart + /// List numbers = [1, 2, 3, 4, 5]; + /// int? firstEven = numbers.find((element) => element % 2 == 0); + /// print(firstEven); // 输出:2 + /// + /// int? inRangeEven = numbers.find((element) => element % 2 == 0, startIndex: 1, endIndex: 4); + /// print(inRangeEven); // 输出:2,因为在1到4的范围内2是第一个偶数 + /// ``` + T? find(bool Function(T) test, {int startIndex = 0, int? endIndex}) { + endIndex ??= length; // 如果endIndex没有提供,默认为列表长度 + for (var i = startIndex; i < endIndex && i < length; i++) { + if (test(this[i])) { + return this[i]; + } + } + return null; + } +} diff --git a/lib/src/util/log.dart b/tdesign-component/lib/src/util/log.dart similarity index 100% rename from lib/src/util/log.dart rename to tdesign-component/lib/src/util/log.dart diff --git a/lib/src/util/map_ext.dart b/tdesign-component/lib/src/util/map_ext.dart similarity index 100% rename from lib/src/util/map_ext.dart rename to tdesign-component/lib/src/util/map_ext.dart diff --git a/lib/src/util/platform_util.dart b/tdesign-component/lib/src/util/platform_util.dart similarity index 86% rename from lib/src/util/platform_util.dart rename to tdesign-component/lib/src/util/platform_util.dart index 4fa4879f3..d62c8563e 100644 --- a/lib/src/util/platform_util.dart +++ b/tdesign-component/lib/src/util/platform_util.dart @@ -23,6 +23,10 @@ class PlatformUtil { return !kIsWeb && Platform.isMacOS; } + static bool get isOhos { + return !kIsWeb && Platform.operatingSystem == 'ohos'; + } + static bool get isWindows { return !kIsWeb && Platform.isWindows; } diff --git a/tdesign-component/lib/src/util/string_util.dart b/tdesign-component/lib/src/util/string_util.dart new file mode 100644 index 000000000..8e548825a --- /dev/null +++ b/tdesign-component/lib/src/util/string_util.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +import 'log.dart'; + +Color? toColor(String colorStr, {double alpha = 1}) { + try { + var hexColor = colorStr.toUpperCase().replaceAll('#', ''); + if (hexColor.length == 6) { + if (alpha < 0) { + alpha = 0; + } else if (alpha > 1) { + alpha = 1; + } + var alphaInt = (0xFF * alpha).toInt(); + var alphaString = alphaInt.toRadixString(16); + + hexColor = '$alphaString$hexColor'; + } + return Color(int.parse(hexColor, radix: 16)); + } catch (e) { + // Log.w('toColor', 'error: $e'); + } + return null; +} diff --git a/tdesign-component/lib/src/util/throttle.dart b/tdesign-component/lib/src/util/throttle.dart new file mode 100644 index 000000000..adf1e88a4 --- /dev/null +++ b/tdesign-component/lib/src/util/throttle.dart @@ -0,0 +1,23 @@ +import 'dart:async'; + +import 'package:flutter/widgets.dart'; + +class Throttle { + Duration delay; + Timer? _timer; + + Throttle({required this.delay}); + + void call(VoidCallback callback) { + if (_timer?.isActive ?? false) { + // 如果 _timer 正在运行,则不执行任何操作 + return; + } + + // 创建一个新的计时器 + _timer = Timer(delay, () { + callback(); + _timer = null; // 计时器执行完毕后,将其置为 null + }); + } +} \ No newline at end of file diff --git a/tdesign-component/lib/src/util/version_util.dart b/tdesign-component/lib/src/util/version_util.dart new file mode 100644 index 000000000..456fdb49d --- /dev/null +++ b/tdesign-component/lib/src/util/version_util.dart @@ -0,0 +1,51 @@ +import 'dart:io' show Platform; + +import 'log.dart'; +import 'platform_util.dart'; + +/// flutter web 是否在Dart 3.2.0版本之后 +bool kIsFlutterWebAfter320 = false; + +class VersionUtil { + static String getDartVersion(){ + return Platform.version.split(' ').first; + } + + /// 当前版本号是否大于等于目标版本号 + static bool isAfterThen(String target){ + try { + if(PlatformUtil.isWeb){ + // TODO: 需要适配flutter web的具体逻辑 + return kIsFlutterWebAfter320; + } + var targets = target.split('.'); + var currents = getDartVersion().split('.'); + if(targets.length != currents.length){ + Log.w('VersionUtil', 'targets.length != currents.length'); + return false; + } + for(var i = 0; i < targets.length; i++){ + var targetVersion = int.parse(targets[i]); + var currentVersion = int.parse(currents[i]); + if(targetVersion == currentVersion){ + continue; + } + return currentVersion > targetVersion; + } + return true; + } catch (e) { + Log.e('VersionUtil', 'isAfterThen target: $target, error: $e'); + } + return false; + } +} + +// void main(){ +// print(VersionUtil.isAfterThen('2.19.6')); +// print(VersionUtil.isAfterThen('2.19.5')); +// print(VersionUtil.isAfterThen('2.19.7')); +// print(VersionUtil.isAfterThen('2.18.7')); +// print(VersionUtil.isAfterThen('2.20.5')); +// print(VersionUtil.isAfterThen('2.19')); +// print(VersionUtil.isAfterThen('2.20.9.1')); +// } \ No newline at end of file diff --git a/tdesign-component/lib/tdesign_flutter.dart b/tdesign-component/lib/tdesign_flutter.dart new file mode 100644 index 000000000..d08adb7a8 --- /dev/null +++ b/tdesign-component/lib/tdesign_flutter.dart @@ -0,0 +1,106 @@ +export 'src/components/action_sheet/td_action_sheet.dart'; +export 'src/components/avatar/td_avatar.dart'; +export 'src/components/backtop/td_backtop.dart'; +export 'src/components/badge/td_badge.dart'; +export 'src/components/button/td_button.dart'; +export 'src/components/button/td_button_style.dart'; +export 'src/components/calendar/td_calendar.dart'; +export 'src/components/cascader/td_cascader.dart'; +export 'src/components/cascader/td_cascader_action.dart'; +export 'src/components/cascader/td_custom_tab.dart'; +export 'src/components/cascader/td_multi_cascader.dart'; +export 'src/components/cell/td_cell.dart'; +export 'src/components/cell/td_cell_group.dart'; +export 'src/components/cell/td_cell_style.dart'; +export 'src/components/checkbox/td_check_box.dart'; +export 'src/components/checkbox/td_check_box_group.dart'; +export 'src/components/collapse/td_collapse.dart'; +export 'src/components/collapse/td_collapse_panel.dart'; +export 'src/components/dialog/td_dialog.dart'; +export 'src/components/divider/td_divider.dart'; +export 'src/components/drawer/td_drawer.dart'; +export 'src/components/drawer/td_drawer_widget.dart'; +export 'src/components/dropdown_menu/td_dropdown_item.dart'; +export 'src/components/dropdown_menu/td_dropdown_menu.dart'; +export 'src/components/empty/td_empty.dart'; +export 'src/components/fab/td_fab.dart'; +export 'src/components/footer/td_footer.dart'; +export 'src/components/form/td_form.dart'; +export 'src/components/form/td_form_item.dart'; +export 'src/components/icon/td_icons.dart'; +export 'src/components/image/image_widget.dart'; +export 'src/components/image/td_image.dart'; +export 'src/components/image_viewer/td_image_viewer.dart'; +export 'src/components/image_viewer/td_image_viewer_widget.dart'; +export 'src/components/indexes/td_indexes.dart'; +export 'src/components/input/input_view.dart'; +export 'src/components/input/td_input.dart'; +export 'src/components/input/td_input_spacer.dart'; +export 'src/components/link/td_link.dart'; +export 'src/components/loading/td_circle_indicator.dart'; +export 'src/components/loading/td_loading.dart'; +export 'src/components/loading/td_loading_controller.dart'; +export 'src/components/message/td_message.dart'; +export 'src/components/navbar/td_nav_bar.dart'; +export 'src/components/notice_bar/td_notice_bar.dart'; +export 'src/components/notice_bar/td_notice_bar_style.dart'; +export 'src/components/picker/td_date_picker.dart'; +export 'src/components/picker/td_item_widget.dart'; +export 'src/components/picker/td_multi_picker.dart'; +export 'src/components/picker/td_picker.dart'; +export 'src/components/popover/td_popover.dart'; +export 'src/components/popover/td_popover_widget.dart'; +export 'src/components/popup/td_popup_panel.dart'; +export 'src/components/popup/td_popup_route.dart'; +export 'src/components/progress/td_progress.dart'; +export 'src/components/radio/td_radio.dart'; +export 'src/components/rate/td_rate.dart'; +export 'src/components/refresh/td_refresh_header.dart'; +export 'src/components/result/td_result.dart'; +export 'src/components/search/td_search_bar.dart'; +export 'src/components/sidebar/td_sidebar.dart'; +export 'src/components/sidebar/td_sidebar_controller.dart'; +export 'src/components/sidebar/td_sidebar_item.dart'; +export 'src/components/skeleton/td_skeleton.dart'; +export 'src/components/skeleton/td_skeleton_rowcol.dart'; +export 'src/components/slider/td_slider.dart'; +export 'src/components/slider/td_slider_theme.dart'; +export 'src/components/stepper/td_stepper.dart'; +export 'src/components/steps/td_steps.dart'; +export 'src/components/swipe_cell/td_swipe_cell.dart'; +export 'src/components/swipe_cell/td_swipe_cell_action.dart'; +export 'src/components/swipe_cell/td_swipe_cell_inherited.dart'; +export 'src/components/swipe_cell/td_swipe_cell_panel.dart'; +export 'src/components/swiper/td_page_transform.dart'; +export 'src/components/swiper/td_swiper.dart'; +export 'src/components/switch/td_switch.dart'; +export 'src/components/tabbar/td_bottom_tab_bar.dart'; +export 'src/components/table/td_table.dart'; +export 'src/components/table/td_table_col.dart'; +export 'src/components/table/td_table_empty.dart'; +export 'src/components/tabs/td_tab.dart'; +export 'src/components/tabs/td_tab_bar.dart'; +export 'src/components/tabs/td_tab_bar_view.dart'; +export 'src/components/tag/td_select_tag.dart'; +export 'src/components/tag/td_tag.dart'; +export 'src/components/tag/td_tag_styles.dart'; +export 'src/components/text/td_font_loader.dart'; +export 'src/components/text/td_text.dart'; +export 'src/components/textarea/td_textarea.dart'; +export 'src/components/time_counter/td_time_counter.dart'; +export 'src/components/time_counter/td_time_counter_controller.dart'; +export 'src/components/time_counter/td_time_counter_style.dart'; +export 'src/components/toast/td_toast.dart'; +export 'src/components/tree/td_tree_select.dart'; +export 'src/components/upload/td_upload.dart'; +export 'src/theme/basic.dart'; +export 'src/theme/resource_delegate.dart'; +export 'src/theme/td_colors.dart'; +export 'src/theme/td_font_family.dart'; +export 'src/theme/td_fonts.dart'; +export 'src/theme/td_radius.dart'; +export 'src/theme/td_shadows.dart'; +export 'src/theme/td_spacers.dart'; +export 'src/theme/td_theme.dart'; +export 'src/util/platform_util.dart'; +export 'src/components/form/td_form_validation.dart'; diff --git a/tdesign-component/publish.sh b/tdesign-component/publish.sh new file mode 100644 index 000000000..e88699f2d --- /dev/null +++ b/tdesign-component/publish.sh @@ -0,0 +1 @@ +~/flutter/bin/flutter packages pub publish --server=https://pub.dartlang.org \ No newline at end of file diff --git a/tdesign-component/pubspec.yaml b/tdesign-component/pubspec.yaml new file mode 100644 index 000000000..c445d5f67 --- /dev/null +++ b/tdesign-component/pubspec.yaml @@ -0,0 +1,81 @@ +name: tdesign_flutter +description: Tencent TDesign UI component library of Flutter, suitable for use in mobile projects. +version: 0.2.3 +homepage: https://github.com/Tencent/tdesign-flutter + +environment: + sdk: ">=3.2.6 <4.0.0" + flutter: ">=3.16.0" + +dependencies: + flutter: + sdk: flutter + collection: + + # 轮播图组件 + flutter_swiper_null_safety: ^1.0.2 + + # 下拉刷新组件 + easy_refresh: ^3.4.0 + + # 滑动单元格组件 + flutter_slidable: 3.1.2 + + tdesign_flutter_adaptation: 3.32.0 + + image_picker: 1.1.2 + +dev_dependencies: + flutter_lints: ^1.0.4 + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter. +flutter: + # This section identifies this Flutter project as a plugin project. + # The 'pluginClass' and Android 'package' identifiers should not ordinarily + # be modified. They are used by the tooling to maintain consistency when + # adding or updating assets for this project. + uses-material-design: true + fonts: + - family: TDIcons + fonts: + - asset: assets/tdesign/td_icons.ttf + - family: TCloudNumber + fonts: + - asset: assets/tdesign/TCloudNumberVF.ttf + + + # To add assets to your plugin package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/assets-and-images/#from-packages + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware. + + # To add custom fonts to your plugin package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/custom-fonts/#from-packages diff --git a/test/flutter_component_test.dart b/tdesign-component/test/flutter_component_test.dart similarity index 100% rename from test/flutter_component_test.dart rename to tdesign-component/test/flutter_component_test.dart diff --git a/tdesign-site/.editorconfig b/tdesign-site/.editorconfig new file mode 100644 index 000000000..f995fe1d9 --- /dev/null +++ b/tdesign-site/.editorconfig @@ -0,0 +1,15 @@ +# http://editorconfig.org +root = true +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = 100 +trim_trailing_whitespace = true +[*.md] +max_line_length = 0 +trim_trailing_whitespace = false +[COMMIT_EDITMSG] +max_line_length = 0 diff --git a/tdesign-site/.eslintignore b/tdesign-site/.eslintignore new file mode 100644 index 000000000..29eab6c33 --- /dev/null +++ b/tdesign-site/.eslintignore @@ -0,0 +1,5 @@ +**/*.wxs +src/common/lib +miniprogram_dist +node_modules +common \ No newline at end of file diff --git a/tdesign-site/.eslintrc.js b/tdesign-site/.eslintrc.js new file mode 100644 index 000000000..c9ea7f852 --- /dev/null +++ b/tdesign-site/.eslintrc.js @@ -0,0 +1,105 @@ +const compileTimeConfiguration = require('./script/config.js'); + +// 配置小程序内全局函数,避免报错 +const globals = { + require: true, + Page: true, + wx: true, + App: true, + getApp: true, + getCurrentPages: true, + Component: true, + getRegExp: true, + Behavior: true, +}; + +// 配置编译时配置,避免报错 +Object.keys(compileTimeConfiguration).forEach((key) => { + globals[key] = true; +}); + +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module', + ecmaFeatures: { + impliedStrict: true, + }, + }, + env: { + browser: true, + node: true, + es6: true, + jest: true, + }, + // 启用默认核心规则 + extends: ['eslint-config-airbnb-base', 'eslint-config-prettier'], + plugins: ['@typescript-eslint', 'prettier', 'import'], + // add your custom rules here + rules: { + // 非开发模式禁用debugger + 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'warn', + // 允许调用首字母大写的函数时没有 new 操作符 + 'new-cap': 'off', + // 在工具库中允许变量以下划线开头 + 'no-underscore-dangle': 'off', + // 在工具库中允许参数重新赋值 + 'no-param-reassign': 'off', + 'number-leading-zero': 'off', + // 在类属性和方法上关闭需要显式的可访问性修饰符 + '@typescript-eslint/explicit-member-accessibility': 'off', + eqeqeq: [ + 'error', + 'always', + { + null: 'ignore', + }, + ], + 'import/no-unresolved': 0, + 'import/no-named-as-default': 0, + 'import/extensions': 0, + 'import/export': 0, + 'import/no-cycle': 0, + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: true, + }, + ], + 'import/no-dynamic-require': 0, + 'object-shorthand': 0, + 'no-shadow': 0, + 'no-unused-expressions': 0, + 'no-unused-vars': 0, + '@typescript-eslint/no-unused-vars': 2, + 'consistent-return': 0, + 'no-return-assign': 0, + 'func-names': 0, + 'class-methods-use-this': 0, + 'no-console': [ + 2, + { + allow: ['warn', 'error'], + }, + ], + 'no-undef': 0, + 'no-proto': 0, + }, + globals, + overrides: [ + { + files: ['script/**'], + rules: { + // node 环境下支持 require + '@typescript-eslint/no-require-imports': 'off', + }, + }, + { + files: ['example/**', '**/_example/**'], + rules: { + 'no-console': 0, + }, + }, + ], +}; diff --git a/tdesign-site/.husky/commit-msg b/tdesign-site/.husky/commit-msg new file mode 100755 index 000000000..fe4c17a22 --- /dev/null +++ b/tdesign-site/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx --no-install commitlint --edit "" diff --git a/tdesign-site/.husky/pre-commit b/tdesign-site/.husky/pre-commit new file mode 100755 index 000000000..c37466e2b --- /dev/null +++ b/tdesign-site/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx lint-staged \ No newline at end of file diff --git a/tdesign-site/.husky/prepare-commit-msg b/tdesign-site/.husky/prepare-commit-msg new file mode 100755 index 000000000..71fe5b35a --- /dev/null +++ b/tdesign-site/.husky/prepare-commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +exec < /dev/tty && npx git-cz --hook || true \ No newline at end of file diff --git a/tdesign-site/.npmignore b/tdesign-site/.npmignore new file mode 100644 index 000000000..9a99e4d50 --- /dev/null +++ b/tdesign-site/.npmignore @@ -0,0 +1,4 @@ +**/** +!LICENSE +!README.md +!CHANGELOG.md diff --git a/tdesign-site/.prettierignore b/tdesign-site/.prettierignore new file mode 100644 index 000000000..0a353edc8 --- /dev/null +++ b/tdesign-site/.prettierignore @@ -0,0 +1 @@ +**/*.md \ No newline at end of file diff --git a/tdesign-site/.prettierrc.yml b/tdesign-site/.prettierrc.yml new file mode 100644 index 000000000..a13251c35 --- /dev/null +++ b/tdesign-site/.prettierrc.yml @@ -0,0 +1,49 @@ +# 一行最多 100 字符 +printWidth: 120 +# 使用 2 个空格缩进 +tabWidth: 2 +# 不使用缩进符,而使用空格 +useTabs: false +# 行尾需要分号 +semi: true +# 使用单引号 +singleQuote: true +# 对象的 key 仅在必要时用引号 +quoteProps: as-needed +# jsx 不使用单引号,而使用双引号 +jsxSingleQuote: false +# 末尾需要逗号 +trailingComma: all +# 大括号内的首尾需要空格 +bracketSpacing: true +# jsx 标签的反尖括号需要换行 +jsxBracketSameLine: false +# 箭头函数,只有一个参数的时候,不需要括号 +arrowParens: always +# 每个文件格式化的范围是文件的全部内容 +rangeStart: 0 +# 不需要写文件开头的 @prettier +requirePragma: false +# 不需要自动在文件开头插入 @prettier +insertPragma: false +# 使用默认的折行标准 +proseWrap: preserve +# 根据显示样式决定 html 要不要折行 +htmlWhitespaceSensitivity: css +# 换行符使用 lf +endOfLine: lf +# 后缀文件名特有规则 +overrides: + - files: '*.{wxss,less}' + options: + parser: less + - files: '*.json,.*rc' + options: + parser: json + - files: '*.{wxml,html}' + options: + parser: html + htmlWhitespaceSensitivity: strict + - files: '*.wxs' + options: + parser: babel diff --git a/tdesign-site/.templates/$$var_template/$$var_filename.js b/tdesign-site/.templates/$$var_template/$$var_filename.js new file mode 100644 index 000000000..6420b1220 --- /dev/null +++ b/tdesign-site/.templates/$$var_template/$$var_filename.js @@ -0,0 +1 @@ +Component({}) \ No newline at end of file diff --git a/tdesign-site/.templates/$$var_template/$$var_filename.json b/tdesign-site/.templates/$$var_template/$$var_filename.json new file mode 100644 index 000000000..53902317a --- /dev/null +++ b/tdesign-site/.templates/$$var_template/$$var_filename.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-$$var_textInFile": "tdesign-miniprogram/$$var_textInFile/$$var_textInFile" + } +} \ No newline at end of file diff --git a/tdesign-site/.templates/$$var_template/$$var_filename.wxml b/tdesign-site/.templates/$$var_template/$$var_filename.wxml new file mode 100644 index 000000000..427afe3ed --- /dev/null +++ b/tdesign-site/.templates/$$var_template/$$var_filename.wxml @@ -0,0 +1 @@ + diff --git a/tdesign-site/.templates/$$var_template/$$var_filename.wxss b/tdesign-site/.templates/$$var_template/$$var_filename.wxss new file mode 100644 index 000000000..e69de29bb diff --git a/tdesign-site/.templates/.editorconfig b/tdesign-site/.templates/.editorconfig new file mode 100644 index 000000000..689fb502a --- /dev/null +++ b/tdesign-site/.templates/.editorconfig @@ -0,0 +1,21 @@ +# @see https://editorconfig-specification.readthedocs.io/en/latest/ + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +charset = utf-8 + +# 4 space indentation +[*.py] +indent_style = space +indent_size = 4 + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab diff --git a/tdesign-site/.templates/_example/$$var_filename.js b/tdesign-site/.templates/_example/$$var_filename.js new file mode 100644 index 000000000..6420b1220 --- /dev/null +++ b/tdesign-site/.templates/_example/$$var_filename.js @@ -0,0 +1 @@ +Component({}) \ No newline at end of file diff --git a/tdesign-site/.templates/_example/$$var_filename.json b/tdesign-site/.templates/_example/$$var_filename.json new file mode 100644 index 000000000..32640e0dc --- /dev/null +++ b/tdesign-site/.templates/_example/$$var_filename.json @@ -0,0 +1,3 @@ +{ + "component": true +} \ No newline at end of file diff --git a/tdesign-site/.templates/_example/$$var_filename.wxml b/tdesign-site/.templates/_example/$$var_filename.wxml new file mode 100644 index 000000000..b1a96ad77 --- /dev/null +++ b/tdesign-site/.templates/_example/$$var_filename.wxml @@ -0,0 +1,3 @@ + + + diff --git a/tdesign-site/.templates/_example/$$var_filename.wxss b/tdesign-site/.templates/_example/$$var_filename.wxss new file mode 100644 index 000000000..e69de29bb diff --git a/tdesign-site/.vscode/component.code-snippets b/tdesign-site/.vscode/component.code-snippets new file mode 100644 index 000000000..16014ccb7 --- /dev/null +++ b/tdesign-site/.vscode/component.code-snippets @@ -0,0 +1,14 @@ +{ + "component-json-template": { + "prefix": "cjson", + "body": ["{", " \"usingComponents\": {", " \"t-$1\": \"tdeisgn-miniprogram/$1/$1\"", " }", "}"], + "scope": "json", + "description": "组件 JSON" + }, + "component-js-template": { + "prefix": "ccjs", + "body": ["Component({});", ""], + "scope": "javascript, typescript", + "description": "组件 JS" + } +} diff --git a/tdesign-site/.vscode/launch.json b/tdesign-site/.vscode/launch.json new file mode 100644 index 000000000..ec06f201d --- /dev/null +++ b/tdesign-site/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "command": "npm run test:unit popup", + "name": "Run unit test", + "request": "launch", + "type": "node-terminal" + }, + ] +} \ No newline at end of file diff --git a/tdesign-site/.vscode/new.code-snippets b/tdesign-site/.vscode/new.code-snippets new file mode 100644 index 000000000..045b438b0 --- /dev/null +++ b/tdesign-site/.vscode/new.code-snippets @@ -0,0 +1,37 @@ +{ + "component-json-template": { + "prefix": "newts", + "body": [ + "import { SuperComponent, wxComponent } from '../common/src/index';", + "import config from '../common/config';", + "import props from './props';", + "", + "const { prefix } = config;", + "const name = `\\${prefix}-$1`;", + "", + "@wxComponent()", + "export default class $2 extends SuperComponent {", + " externalClasses = [", + " `\\${prefix}-class`,", + " ];", + "", + " properties = props;", + "", + " observers = {", + " };", + "", + " data = {", + " classPrefix: name,", + " prefix,", + " };", + "", + " methods = {", + " ", + " }", + "}", + "", + ], + "scope": "typescript", + "description": "新组件 ts" + }, +} \ No newline at end of file diff --git a/tdesign-site/.vscode/settings.json b/tdesign-site/.vscode/settings.json new file mode 100644 index 000000000..d764cd678 --- /dev/null +++ b/tdesign-site/.vscode/settings.json @@ -0,0 +1,36 @@ +{ + "editor.formatOnSave": true, + "eslint.enable": true, + "editor.codeActionsOnSave": { + "source.fixAll.tslint": "explicit", + "source.fixAll.eslint": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[javascript]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "minapp-vscode.reserveTags": ["text"], + "files.associations": { + "*.wxss": "css", + "*.wxs": "javascript" + }, + "typescript.tsdk": "node_modules/typescript/lib", + "[json]": { + "editor.defaultFormatter": "vscode.json-language-features" + }, + "minapp-vscode.wxmlFormatter": "prettier", + "[wxml]": { + "editor.defaultFormatter": "qiu8310.minapp-vscode" + }, + "css.validate": false, + "less.validate": false, + "scss.validate": false, + "[less]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "cSpell.words": ["stylelint"] +} diff --git a/tdesign-site/.vscode/test.code-snippets b/tdesign-site/.vscode/test.code-snippets new file mode 100644 index 000000000..4d9a50ed3 --- /dev/null +++ b/tdesign-site/.vscode/test.code-snippets @@ -0,0 +1,31 @@ +{ + "test-template": { + "prefix": "ttest", + "body": [ + "import simulate from 'miniprogram-simulate';", + "import path from 'path';", + "", + "describe('$1', () => {", + " const $1 = simulate.load(path.resolve(__dirname, `../$1`), 't-$1', {", + " less: true,", + " rootPath: path.resolve(__dirname, '../..')", + " });", + "", + " it(':$2', () => {", + " const id = simulate.load({", + " template: ``,", + " usingComponents: {", + " 't-$1': $1", + " }", + " })", + " const comp = simulate.render(id);", + " comp.attach(document.createElement('parent-wrapper'));", + "", + " expect(comp.toJSON()).toMatchSnapshot();", + " });", + "});", + ], + "scope": "javascript,typescript", + "description": "测试用例模板" + } +} \ No newline at end of file diff --git a/tdesign-site/CHANGELOG.md b/tdesign-site/CHANGELOG.md new file mode 100644 index 000000000..ba11c8d11 --- /dev/null +++ b/tdesign-site/CHANGELOG.md @@ -0,0 +1,434 @@ +--- +title: 更新日志 +spline: explain +toc: false +docClass: timeline +--- + + +## 🌈 0.2.3 `2025-07-09` +### 🚀 Features +- `TDPicker`: 支持切换时优先保持级联的选项值 @epoll-j ([#666](https://github.com/Tencent/tdesign-flutter/pull/666)) +- `TDTable`: 支持行默认选中 @ccXxx1aoBai ([#665](https://github.com/Tencent/tdesign-flutter/pull/665)) +- `TDCalendar`: 增加自定义日期单元格功能 @epoll-j ([#667](https://github.com/Tencent/tdesign-flutter/pull/667)) +- `TDForm`: 增加Form 表单组件 @shizhe2018 @SimonWuZY ([#620](https://github.com/Tencent/tdesign-flutter/pull/620)) +- `TDTable`: TDTableCol属性配置分离,空数据配置分离 @ccXxx1aoBai ([#665](https://github.com/Tencent/tdesign-flutter/pull/665)) +### 🐞 Bug Fixes +- `TDTable`: 解决表头未选中图标显示问题,禁用状态下全选选中状态问题 @ccXxx1aoBai ([#665](https://github.com/Tencent/tdesign-flutter/pull/665)) +- `TDTable`: 表格空数据问题 @ccXxx1aoBai ([#671](https://github.com/Tencent/tdesign-flutter/pull/671)) +- `TDDialog`: 弹窗遮挡键盘问题 @jflin19990707 ([#669](https://github.com/Tencent/tdesign-flutter/pull/669)) +- `TDCollapse`: collapse demo页面名称修改 @jflin19990707 ([#670](https://github.com/Tencent/tdesign-flutter/pull/670)) +- `TDDropdownMenu`: 嵌套路由场景 弹窗位置计算错误 @hcanyz ([#648](https://github.com/Tencent/tdesign-flutter/pull/648)) + +## 🌈 0.2.2 `2025-06-13` + +### 🚀 Features + +- `TDTable`: 支持表格行选择、自定义行高 @ccXxx1aoBai ([#594](https://github.com/Tencent/tdesign-flutter/pull/594)) +- `TDTreeSelect`: 支持局部多选 @epoll-j ([#587](https://github.com/Tencent/tdesign-flutter/pull/587)) +- `TDCell`: 支持自定义高度,底部分割线 @ccXxx1aoBai ([#611](https://github.com/Tencent/tdesign-flutter/pull/611)) +- `TDNoticeBar`: 支持自定义文字行数 @ccXxx1aoBai ([#611](https://github.com/Tencent/tdesign-flutter/pull/611)) +- `TDBottomTabBar`: TDButtonBottomTabBar 中的 onTap 支持重复点击 @epoll-j @RSS1102([#586](https://github.com/Tencent/tdesign-flutter/pull/586)) +- `TDBottomTabBar`: 实现点击水波纹效果 @RSS1102 ([#626](https://github.com/Tencent/tdesign-flutter/pull/626)) +- `TDAvatar` 增加自定义BoxFit参数 @shizhe2018 ([#633](https://github.com/Tencent/tdesign-flutter/pull/633)) + +### 🐞 Bug Fixes + +- `TDDatePicker`: 修复时间选择器分钟级时间数据展示问题- 优化小时、分钟、秒的选择范围计算逻辑 @epoll-j ([#585](https://github.com/Tencent/tdesign-flutter/pull/585)) +- `TDSearchBar`: 支持设置onTapOutside回调 @cyjaysong ([#608](https://github.com/Tencent/tdesign-flutter/pull/608)) +- `TDDropdownMenu`: 支持修改选中icon颜色 @jflin19990707 ([#631](https://github.com/Tencent/tdesign-flutter/pull/631)) +- `TDTabBar`: fix:TDBottomTabBarBasicType.iconText模式下,text icon 冲突问题 @jflin19990707 ([#628](https://github.com/Tencent/tdesign-flutter/pull/628)) +- `TDEmpty`: 支持操作按钮自定义样式 @jflin19990707 ([#624](https://github.com/Tencent/tdesign-flutter/pull/624)) +- `TDToast`: toast支持自定义文案 @jflin19990707 ([#625](https://github.com/Tencent/tdesign-flutter/pull/625)) +- `TDPopup`: 修改_measureChildHeight方法用于修复child无法修改弹窗高度 @Jzow ([#591](https://github.com/Tencent/tdesign-flutter/pull/591)) +- `TDCascader` 修改查询data数据为空状态处理 @shizhe2018 ([#635](https://github.com/Tencent/tdesign-flutter/pull/635)) + +### 🚧 Others + +- [其他]适配flutter 3.32版本 @Luozf12345 ([#636](https://github.com/Tencent/tdesign-flutter/pull/636)) + + + +## 🌈 0.2.0 `2025-05-08` +### 🚀 Features +- `TDCellGroup`: 添加单元格组标题背景颜色`titleBackgroundColor`属性. @runoob-coder ([#539](https://github.com/Tencent/tdesign-flutter/pull/539)) +- `TDLink`: link参数链接对象`LinkObj`替换为`MessageLink`,调整`TDLink`样式,新增点击回调; @runoob-coder ([#554](https://github.com/Tencent/tdesign-flutter/pull/554)) +- `TDBottomTabBar`: 新增自定义标题支持到步骤条组件 @RSS1102 ([#576](https://github.com/Tencent/tdesign-flutter/pull/576)) +- `TDSlider`: 添加滑块点击事件 `onTap` @RSS1102 ([#527](https://github.com/Tencent/tdesign-flutter/pull/527)) +- `TDCascader`: 添加右上角"确定"按钮,支持选择任意选项 @Luozf12345 +- `ImageViewer`: 支持单张图片删除 @ccXxx1aoBai ([#581](https://github.com/Tencent/tdesign-flutter/pull/581)) +- `TDPopup`: 为Popup组件添加标题、左文本、右文本和关闭按钮自定义尺寸属性 @Jzow ([#582](https://github.com/Tencent/tdesign-flutter/pull/582)) +- `TDBottomTabBarTabConfig`: 添加长按 tab 触发事件`onLongPress` @RSS1102 ([#580](https://github.com/Tencent/tdesign-flutter/pull/580)) + +### 🐞 Bug Fixes +- `TDFooter`: 修复页脚链接模式时内容溢出问题 @runoob-coder ([#554](https://github.com/Tencent/tdesign-flutter/pull/554)) +- `TDUpload`: 修复文件大小限制错误 @epoll-j ([#544](https://github.com/Tencent/tdesign-flutter/pull/544)) +- `TDImageViewer`: 增加Swiper组件属性透传,增加点击事件及部分样式属性,支持自定义按钮 @ccXxx1aoBai ([#561](https://github.com/Tencent/tdesign-flutter/pull/561)) +- `TDSlider`: 修复是胶囊类型且有区间时,滑块无法拖动到边缘以及数值和刻度展示问题 @qfish ([#567](https://github.com/Tencent/tdesign-flutter/pull/567)) +- `TDInput`: 修复非中文标签Input框宽度计算缺陷 @Jzow ([#564](https://github.com/Tencent/tdesign-flutter/pull/564)) +- `TDPopup`: 修复无法通过child中的height来修改弹出层高度 @Jzow ([#571](https://github.com/Tencent/tdesign-flutter/pull/571)) +- `TDDropdownMenu`: 修复特定情况下的单选失效 @1jialong ([#575](https://github.com/Tencent/tdesign-flutter/pull/575)) +- `TDToast`: 修复Toast多行文字不生效的问题 @Luozf12345 +- `TDPopup`: 修复Popup外层没有Scaffold时展示文字有横线的问题 @Luozf12345 + +### 🚧 Others +- `TDFooter`: 重构 `TDFooter` 组件;将 `LinkObj` 类移除,直接使用 `TDLink` 类;移除了 `isWithUnderline` 参数,改为在 `TDLink` 中设置链接样式; @runoob-coder ([#554](https://github.com/Tencent/tdesign-flutter/pull/554)) + + + +## 🌈 0.1.9 `2025-03-31` +### 🚀 Features +- `TDProgress`: 新增`Progress 进度条`组件 @CORCTON ([#307](https://github.com/Tencent/tdesign-flutter/pull/307)) +- `TDMessage`: 新增`Message 全局提示`组件 @chendingya ([#316](https://github.com/Tencent/tdesign-flutter/pull/316)) +- `TDSkeleton`: 新增`Skeleton 骨架屏`组件 @Ezer015 ([#317](https://github.com/Tencent/tdesign-flutter/pull/317)) +- `TDFooter`: 新增`Footer 页脚`组件 @chendingya ([#224](https://github.com/Tencent/tdesign-flutter/pull/224)) +- `TDPopover`: 新增`Popover 弹出气泡`组件 @ccXxx1aoBai ([#435](https://github.com/Tencent/tdesign-flutter/pull/435)) +- `TDSwitch`: 添加自定义“开/关”字体大小 @shinyina ([#217](https://github.com/Tencent/tdesign-flutter/pull/217)) +- `TDDatePicker`: filterItems 参数添加,可自定义显示那些选项;itemBuilder 参数添加,用于自定义item @hkaikai ([#426](https://github.com/Tencent/tdesign-flutter/pull/426)) +- `TDDrawer`: 新建TDDrawerWidget组件,可用于Scaffold中的drawer属性 @hkaikai ([#445](https://github.com/Tencent/tdesign-flutter/pull/445)) +- `TDTable`: 自定义列返回当前行号 @ccXxx1aoBai ([#457](https://github.com/Tencent/tdesign-flutter/pull/457)) +- `TDUpload`: upload组件支持宽高设置和快速替换配置 @HubuHito ([#462](https://github.com/Tencent/tdesign-flutter/pull/462)) +- `TDButton`: 添加按钮图标位置属性 @epoll-j ([#463](https://github.com/Tencent/tdesign-flutter/pull/463)) +- `TDDropdownMenu`: 支持单选(multiple == false)模式下,分栏(optionsColumns > 1)展示选项 @hkaikai ([#502](https://github.com/Tencent/tdesign-flutter/pull/502)) +- `TDActionSheet`: 新增动作面板组件 @hkaikai ([#485](https://github.com/Tencent/tdesign-flutter/pull/485)) +- `TDPicker`: 增加customSelectWidget参数 @epoll-j ([#495](https://github.com/Tencent/tdesign-flutter/pull/495)) +- `TDSlider`: 增加修改滑轨颜色参数 @epoll-j ([#506](https://github.com/Tencent/tdesign-flutter/pull/506)) +- `TDCalendar`: 添加动画滚动日历选中值位置 @hkaikai ([#509](https://github.com/Tencent/tdesign-flutter/pull/509)) +- `TDStep`: 新增CustomContent参数支持Step的Content可以自定义内容 @Jzow ([#452](https://github.com/Tencent/tdesign-flutter/pull/452)) +- `TDTag`: 新增fixedWidth参数固定宽度属性,可自定义Tag的宽度,修复TextOverflow.ellipsis溢出title问题 @Jzow ([#496](https://github.com/Tencent/tdesign-flutter/pull/496)) +- `TDPopup`: 为底部浮层面板添加了边缘拖动控制 @Jzow ([#514](https://github.com/Tencent/tdesign-flutter/pull/514)) +- `TDBadge`: Badge设置封顶的数字值 @chendingya ([#302](https://github.com/Tencent/tdesign-flutter/pull/302)) +- `TDToast`:带图标类型支持设置文字行数 @ccXxx1aoBai ([#481](https://github.com/Tencent/tdesign-flutter/pull/481)) + + +### 🐞 Bug Fixes +- `TDRefreshHeader`: 升级easy refresh到最新版本,兼容v2和v3写法;交互同步其他移动端平台 @hkaikai ([#438](https://github.com/Tencent/tdesign-flutter/pull/438)) +- `TDCell`: 修复无默认样式情况下点击空白区域无反应问题;TDCellStyle默认构造方法提供context参数,可以构建默认样式;完善demo自定义样式用法 @hkaikai ([#448](https://github.com/Tencent/tdesign-flutter/pull/448)) +- `TDTable`: 解决空数据图片无法显示问题 @ccXxx1aoBai ([#451](https://github.com/Tencent/tdesign-flutter/pull/451)) +- `TDTabBar`: labelStyle、unselectedLabelStyle支持自定义Label的文字大小 @hkaikai ([#453](https://github.com/Tencent/tdesign-flutter/pull/453)) +- `TDCalendar`: 修复定位到最后一个月时,无法定位问题 @hkaikai ([#449](https://github.com/Tencent/tdesign-flutter/pull/449)) +- `TDBottomTabBar`: 修复capsule类型无法设置背景色 @epoll-j ([#497](https://github.com/Tencent/tdesign-flutter/pull/497)) +- `TDCalendar`: 确定按钮添加国际化 @hkaikai ([#505](https://github.com/Tencent/tdesign-flutter/pull/505)) +- `TDUpload`: 新增onMaxLimitReached函数修复自定义处理文件数量超过最大限制的Bug @Jzow ([#474](https://github.com/Tencent/tdesign-flutter/pull/474)) +- `TDInput`: 新增_getTextWidth函数获取文本实际宽度和点击文本触发事件,修复buildNormalInput文本显示不全 @Jzow ([#475](https://github.com/Tencent/tdesign-flutter/pull/475)) +- `TDImage`: 移除自定义宽和自定义高必填,以及默认高度和宽度尺寸72约束,让布局系统自动计算高度 @Jzow ([#499](https://github.com/Tencent/tdesign-flutter/pull/499)) +- `TDConfirmDialog`: 新增布局约束和滚动支持动态计算最大高度,修复渲染失败Bug @Jzow ([#510](https://github.com/Tencent/tdesign-flutter/pull/510)) +- `TDDrawer`: 新增_deleteRouter()在close函数中的调用强制清除路由,修复关闭路由无法再次打开Bug @Jzow ([#512](https://github.com/Tencent/tdesign-flutter/pull/512)) +- `TDText`: 3.22鸿蒙版本text,组件不居中问题 @duleigiser ([#437](https://github.com/Tencent/tdesign-flutter/pull/437)) +- `TDAlertDialog`: fix 按钮样式没有铺满 @lvjs ([#460](https://github.com/Tencent/tdesign-flutter/pull/460)) + + +### 🚧 Others +- `TDSlider`: 演示代码拆分 @iamitis ([#245](https://github.com/Tencent/tdesign-flutter/pull/245)) +- “关于我们”页面增加发版日期 @iamitis ([#304](https://github.com/Tencent/tdesign-flutter/pull/304)) +- `Doc`: 更新README文件英文,新增License文件和Issue Doc模板 @Jzow ([#458](https://github.com/Tencent/tdesign-flutter/pull/458)) + + +## 🌈 0.1.8 `2024-12-30` +### 🚀 Features +- `TDUpload`: 新增Upload组件 @TingShine ([#405](https://github.com/Tencent/tdesign-flutter/pull/405)) +- `SearchBar`: 增加键盘动作类型 @ccXxx1aoBai ([#366](https://github.com/Tencent/tdesign-flutter/pull/366)) +- `Cell`: CellGroup 新增样式控制参数:cardBorderRadius(卡片模式边框圆角)、cardPadding(卡片模式内边距)、titlePadding(标题内边距) @hkaikai ([#409](https://github.com/Tencent/tdesign-flutter/pull/409)) +- `DropdownMenu`: 新增装饰器配置:decoration,可自定义菜单颜色和边框 @hkaikai ([#408](https://github.com/Tencent/tdesign-flutter/pull/408)) +- `ImageViewer`: 支持显示图片标题 @ccXxx1aoBai ([#411](https://github.com/Tencent/tdesign-flutter/pull/411)) +- `Calendar`: 新增monthTitleBuilder参数 @hkaikai ([#419](https://github.com/Tencent/tdesign-flutter/pull/419)) +- `Calendar`: 新增pickerHeight、pickerItemCount参数,用于控制时间选择组件高度 @hkaikai ([#421](https://github.com/Tencent/tdesign-flutter/pull/421)) +- `Toast`: 支持自定义蒙层背景色 @ccXxx1aoBai ([#423](https://github.com/Tencent/tdesign-flutter/pull/423)) +- `Rate`: 支持disabled 参数 @hkaikai ([#357](https://github.com/Tencent/tdesign-flutter/pull/357)) +- `Calendar`: 修改CalendarBuilder返回值为Widget @Luozf12345 ([#396](https://github.com/Tencent/tdesign-flutter/pull/396)) +- `SearchBar`: 新增只读属性与点击事件 @shizhe2018 ([#393](https://github.com/Tencent/tdesign-flutter/pull/393)) +- `Dialog`: TDDialogButtonOptions新增属性字体大小 @shizhe2018 ([#381](https://github.com/Tencent/tdesign-flutter/pull/381)) +- `DateTimePicker`: 新增时间单位显示属性 @shizhe2018 ([#383](https://github.com/Tencent/tdesign-flutter/pull/383)) +- `Input`: 新增additionInfo 左右显示位置 @shizhe2018 ([#401](https://github.com/Tencent/tdesign-flutter/pull/401)) +### 🐞 Bug Fixes +- `NoticeBar`: 解决web端文字显示异常问题 @ccXxx1aoBai ([#351](https://github.com/Tencent/tdesign-flutter/pull/351)) +- `Rate`: 修复半选时,点击提示框没有触发onChange事件的问题 @hkaikai ([#361](https://github.com/Tencent/tdesign-flutter/pull/361)) +- `Calendar`: 修复因月份日期行数不一致导致回显滚动位置不准确问题 @hkaikai ([#363](https://github.com/Tencent/tdesign-flutter/pull/363)) +- `Calendar`: 优化min、max过大导致渲染卡顿问题 @hkaikai ([#363](https://github.com/Tencent/tdesign-flutter/pull/363)) +- `Input`: 修复设置contentPadding时分割线与内容没对齐问题 @epoll-j ([#365](https://github.com/Tencent/tdesign-flutter/pull/365)) +- `Table`: 解决固定列设置宽度溢出问题 @ccXxx1aoBai ([#370](https://github.com/Tencent/tdesign-flutter/pull/370)) +- `Popup`: 修复点击蒙层关闭延迟问题 @hkaikai ([#380](https://github.com/Tencent/tdesign-flutter/pull/380)) +- `Cascader`: 新增第一层点击选择功能 @shizhe2018 ([#355](https://github.com/Tencent/tdesign-flutter/pull/355)) +- `DateTimePicker`: 新增限制时分秒 @shizhe2018 ([#362](https://github.com/Tencent/tdesign-flutter/pull/362)) +- `Textarea`: 优化字数限制变化更新 @shizhe2018 ([#385](https://github.com/Tencent/tdesign-flutter/pull/385)) +- `TabBar`: 修复labelStyle和unselectedLabelStyle 不生效的问题 @shizhe2018 ([#399](https://github.com/Tencent/tdesign-flutter/pull/399)) +- `Picker`: 修改多层弹框,滑动无法选择颜色问题 @shizhe2018 ([#413](https://github.com/Tencent/tdesign-flutter/pull/413)) +- `SearchBar`: 修复SearchBar聚集时默认位置抖动,以及光标未居中的问题 @Luozf12345 ([#417](https://github.com/Tencent/tdesign-flutter/pull/417)) +- `Dialog`: 修改Dialog可以只传contentWidget,不用传title和content @Luozf12345 ([#418](https://github.com/Tencent/tdesign-flutter/pull/418)) +- `TDBottomTabBar`: 修复iconText模式,底部溢出2.5像素 @epoll-j ([#422](https://github.com/Tencent/tdesign-flutter/pull/422)) +### 🚧 Others +- 适配FlutterSdk3.25,最低支持版本调整为3.16.0 @shizhe2018 ([#378](https://github.com/Tencent/tdesign-flutter/pull/378)) +- 修改Example英文版文案 @shizhe2018 ([#382](https://github.com/Tencent/tdesign-flutter/pull/382)) +- 升级flutter_slidable版本 @Luozf12345 ([#407](https://github.com/Tencent/tdesign-flutter/pull/407)) +- demo增加组件搜索功能 @Luozf12345 ([#410](https://github.com/Tencent/tdesign-flutter/pull/410)) +- 更新Icons @Luozf12345 ([#420](https://github.com/Tencent/tdesign-flutter/pull/420)) + + +## 🌈 0.1.7 `2024-10-16` +### 🚀 Features +- `TDNoticeBar`: 新增noticeBar组件 @ccXxx1aoBai ([#162](https://github.com/Tencent/tdesign-flutter/pull/162)) +- `Result`: 新增Result结果组件 @shinyina ([#220](https://github.com/Tencent/tdesign-flutter/pull/220)) +- `TimeCounter`: 计时组件支持超过转换单位的时间展示,原TDCountDown组件改名为TimeCounter @hkaikai ([#272](https://github.com/Tencent/tdesign-flutter/pull/272)) +- `Calendar`: 新增Calendar 日历组件 @hkaikai ([#271](https://github.com/Tencent/tdesign-flutter/pull/271)) +- `Indexes`: 新增索引组件 @hkaikai ([#321](https://github.com/Tencent/tdesign-flutter/pull/321)) +- `Table`: 新增table组件 @ccXxx1aoBai ([#244](https://github.com/Tencent/tdesign-flutter/pull/244)) +- `Rate`: 新增Rate组件 @ hkaikai ([#338](https://github.com/Tencent/tdesign-flutter/pull/338)) +- `Dialog`: 支持自定义内容内边距和按钮 @ccXxx1aoBai ([#289](https://github.com/Tencent/tdesign-flutter/pull/289)) +- `Drawer`: 支持控制分割线显隐,支持自定义抽屉背景色,支持控制显示最后一条分割线 @ccXxx1aoBai ([#278](https://github.com/Tencent/tdesign-flutter/pull/278)) +- `DropdownMenu`: 新增 图标/宽度/高度/图标和文字的对齐方式 控制参数 @hkaikai ([#297](https://github.com/Tencent/tdesign-flutter/pull/297)) +- `Search`: 增加action和onActionClick属性 @Ezer015 ([#263](https://github.com/Tencent/tdesign-flutter/pull/263)) +- `Avatar`: 增加onTap事件 @ccXxx1aoBai ([#344](https://github.com/Tencent/tdesign-flutter/pull/344)) +- `TDDropdownMenu`: TDDropdownItem新增tabBarFlex参数,控制宽度占比 @hkaikai ([#338](https://github.com/Tencent/tdesign-flutter/pull/338)) +- `SearchBar`:Feature/td searchbarfix 新增光标高属性 @shizhe2018 ([#337](https://github.com/Tencent/tdesign-flutter/pull/337)) +- `TimeCounter`: 添加正向计时功能 @epoll-j ([#246](https://github.com/Tencent/tdesign-flutter/pull/246)) +- `NavBar `:[NavBar]支持设置底部阴影 @ccXxx1aoBai ([#284](https://github.com/Tencent/tdesign-flutter/pull/284)) +- `Cell`: 添加自定义padding参数 @epoll-j ([#276](https://github.com/Tencent/tdesign-flutter/pull/276)) +- `Input`: 增加onTapOutside回调 @epoll-j ([#280](https://github.com/Tencent/tdesign-flutter/pull/280)) +- `Picker`: 增加自定义leftText、rightText @epoll-j ([#301](https://github.com/Tencent/tdesign-flutter/pull/301)) +- `Slider`:Feature/tdslider 新增文本换行功能 @shizhe2018 ([#329](https://github.com/Tencent/tdesign-flutter/pull/329)) +- `Radio`:Feature/tdRadioGroup 新增自带换行,设置行列数 @shizhe2018 ([#331](https://github.com/Tencent/tdesign-flutter/pull/331)) +- `Dialog`:新增自定义输入框 @shizhe2018 ([#333](https://github.com/Tencent/tdesign-flutter/pull/333)) +- `TDNavBar`:添加flexibleSpace参数 @Luozf12345 ([#341](https://github.com/Tencent/tdesign-flutter/pull/341)) +- `TDSearch`:添加搜索框焦点获取及清除事件 @Luozf12345 ([#342](https://github.com/Tencent/tdesign-flutter/pull/342)) + + +### 🐞 Bug Fixes +- `ImageViewer`: 解决defaultIndex无效问题 @ccXxx1aoBai ([#292](https://github.com/Tencent/tdesign-flutter/pull/292)) +- `TimeCounter`: 修复无法重复重置问题 @hkaikai ([#272](https://github.com/Tencent/tdesign-flutter/pull/272)) +- `DropdownMenu`: 调整弹出层逻辑,修复无法监听后退问题; @hkaikai ([#297](https://github.com/Tencent/tdesign-flutter/pull/297)) +- `DatePicker`: 销毁时移除年月日上监控,避免内存泄露;新增onSelectedItemChanged事件 @hkaikai ([#300](https://github.com/Tencent/tdesign-flutter/pull/300)) +- `SideBar`: 解决自定义选中样式文字不居中问题 @ccXxx1aoBai ([#313](https://github.com/Tencent/tdesign-flutter/pull/313)) +- `Popup`: 解决快速点击蒙层多次返回问题 @ccXxx1aoBai ([#318](https://github.com/Tencent/tdesign-flutter/pull/318)) +- `ImageViewer`: 解决删除首位图片显示异常问题 @ccXxx1aoBai ([#322](https://github.com/Tencent/tdesign-flutter/pull/322)) +- `SideBar`: 解决延迟加载组件导致瞄点功能异常问题 @ccXxx1aoBai ([#343](https://github.com/Tencent/tdesign-flutter/pull/343)) +- `TDDropdownMenu`: 优化menu显示文字超出显示省略号 @hkaikai ([#338](https://github.com/Tencent/tdesign-flutter/pull/338)) +- `NoticeBar`: 解决无法跟随主题色问题 @ccXxx1aoBai ([#350](https://github.com/Tencent/tdesign-flutter/pull/350)) +- `Button`: 修复设置shape为square或circle时出现overflow @epoll-j ([#257](https://github.com/Tencent/tdesign-flutter/pull/257)) +- `Slider`: bugfix:修复tb_slider setState不更新问题 @arvinwli ([#298](https://github.com/Tencent/tdesign-flutter/pull/298)) +- `Cascader`: 修改列表排序问题 @shizhe2018 ([#303](https://github.com/Tencent/tdesign-flutter/pull/303)) +- `Popup`:解决键盘出现会遮挡Popup里的输入框 @epoll-j ([#264](https://github.com/Tencent/tdesign-flutter/pull/264)) +- `Cascader`:修改联动时间限制范围逻辑 @shizhe2018 ([#242](https://github.com/Tencent/tdesign-flutter/pull/242)) +- `Loading`:修复Loading显示后立即dismiss无法生效的问题 @Luozf12345 ([#340](https://github.com/Tencent/tdesign-flutter/pull/340)) + + +### 🚧 Others +- fix: remove useless output. @Ives7 ([#311](https://github.com/Tencent/tdesign-flutter/pull/311)) + + + +## 🌈 0.1.6 `2024-07-24` +### 🚀 Features +- `Cell`: 新增 Cell 单元格 组件 @hkaikai ([#150](https://github.com/Tencent/tdesign-flutter/pull/150)) +- `Drawer`: 新增Drawer 抽屉 组件 @hkaikai ([#178](https://github.com/Tencent/tdesign-flutter/pull/178)) +- `SwipeCell`: 新增SwipeCell 滑动操作 组件 @hkaikai ([#218](https://github.com/Tencent/tdesign-flutter/pull/218)) +- `Steps`: 新增 Steps 步骤条 组件 @aaronmhl ([#199](https://github.com/Tencent/tdesign-flutter/pull/199)) +- `ImageViewer`: 新增ImageViewer 图片预览 组件 @ccXxx1aoBai ([#187](https://github.com/Tencent/tdesign-flutter/pull/187)) +- `Cascader`:新增 Cascader 级联选择器 组件@shizhe2018 ([#195](https://github.com/Tencent/tdesign-flutter/pull/195)) +- `Fab`:新增 Fab 悬浮按钮 组件 @TingShine ([#239](https://github.com/Tencent/tdesign-flutter/pull/239)) +- `BackTop`:新增 BackTop 返回顶部 组件 @TingShine ([#239](https://github.com/Tencent/tdesign-flutter/pull/239)) +- `TreeSelect`:新增 TreeSelect 树形选择器 组件 @TingShine ([#239](https://github.com/Tencent/tdesign-flutter/pull/239)) +- `Collapse`:新增 Collapse 折叠面板 组件 @dorayx ([#239](https://github.com/Tencent/tdesign-flutter/pull/239)) +- `Input`: 新增inputAction API,支持设置键盘行为;新增spacer API,可自定义组件间距 @ccXxx1aoBai ([#184](https://github.com/Tencent/tdesign-flutter/pull/184)) +- `Text`: 增加全局字体配置和加载网络字体的能力 @Luozf12345 ([#232](https://github.com/Tencent/tdesign-flutter/pull/232)) +- `CountDown`: 添加 开始/重置/暂停/继续 的控制功能 @hkaikai ([#175](https://github.com/Tencent/tdesign-flutter/pull/175)) +- `Popup`: 支持位置,大小设置 @hkaikai ([#191](https://github.com/Tencent/tdesign-flutter/pull/191)) +### 🐞 Bug Fixes +- `Toast`: 解决duration属性无效问题 @ccXxx1aoBai ([#167](https://github.com/Tencent/tdesign-flutter/pull/167)) +- `Tnput`: 解决label溢出问题 @ccXxx1aoBai ([#184](https://github.com/Tencent/tdesign-flutter/pull/184)) +- `Tabs`:tabs组件outlineType为capsule支持设置选中和未选中tab背景色,outlineType为card支持设置选中tab背景色 @ccXxx1aoBai +- `Button`: 修复setState方法下属性无法改变的问题 @shizhe2018 ([#201](https://github.com/Tencent/tdesign-flutter/pull/201)) +- `SearchBar`:搜索框增加控制器,允许外部清除搜索文本 @shizhe2018 ([#194](https://github.com/Tencent/tdesign-flutter/pull/194)) +- `Slider`: 新增自定义Decoration样式 @shizhe2018 ([#198](https://github.com/Tencent/tdesign-flutter/pull/198)) +- `Empty`: 新增文字大小样式 api @shizhe2018 ([#219](https://github.com/Tencent/tdesign-flutter/pull/219)) +- `Dialog`: 新增input类型背景 @shizhe2018 ([#238](https://github.com/Tencent/tdesign-flutter/pull/238)) +### 🚧 Others +- 鸿蒙编译支持 @hkaikai ([#233](https://github.com/Tencent/tdesign-flutter/pull/233)) +- 修改主题适配工具 @Luozf12345 +- 演示代码新增完整页面的github链接 @Luozf12345 + +## 🌈 0.1.5 `2024-05-31` + +### 🚀 Features +- `TDDropdownMenu`: + - add: 新增TDDropdownMenu 下拉菜单 组件 @hkaikai +- `TDTextarea`: + - add: 新增Textarea 多行文本框 组件 @hkaikai +- `TDBottomTabBar`: + - add:支持自定义背景颜色和icon与文本中间距离([#138](https://github.com/Tencent/tdesign-flutter/issues/138)) + - add:TDBottomTabBar支持外部设置currentIndex ([#110](https://github.com/Tencent/tdesign-flutter/issues/110)) +- `TDBadge`: + - add: TDBadge当值为0时角标显隐设置 @ccXxx1aoBai +- `TDRadio`: + - add: TDRadio增加自定义背景色和文字颜色 @ccXxx1aoBai ([#135](https://github.com/Tencent/tdesign-flutter/issues/135)) + - add: 新增去掉左边边距API([#128](https://github.com/Tencent/tdesign-flutter/issues/128)) +- `TDCheckbox`: + - add: TDCheckbox增加自定义文字颜色 + - add: 新增去掉左边边距API +- `TDImage`: + - add: 新增Image.file([#133](https://github.com/Tencent/tdesign-flutter/issues/133)) + - add: 允许外部自定义TDImage的fit方式([#114](https://github.com/Tencent/tdesign-flutter/issues/114)) +- `TDInput`: + - add: 新增Input清除按钮自定义尺寸 ([#147](https://github.com/Tencent/tdesign-flutter/issues/147)) + - add: 新增label文本左侧间距 ([#147](https://github.com/Tencent/tdesign-flutter/issues/147)) + - add: 新增carType类型的rightWidget ([#147](https://github.com/Tencent/tdesign-flutter/issues/32)) +- `TDDivider`: + - add: 新增分割线组件设置文字样式大小 ([#134](https://github.com/Tencent/tdesign-flutter/issues/134)) +- `TDToast`: + - add: Toast增加自定义文本长度的属性 ([#148](https://github.com/Tencent/tdesign-flutter/issues/148)) +- `TDSideBar`: + - add: 新增选中样式 ([#69](https://github.com/Tencent/tdesign-flutter/issues/69)) + - add: 新增以及自定义文本边距 ([#67](https://github.com/Tencent/tdesign-flutter/issues/67)) + + +### 🐞 Bug Fixes +- `TDButton`: + - fix: setState()前增加mounted判断 ([#122](https://github.com/Tencent/tdesign-flutter/issues/112)) +- `TDDialog`: + - fix: 修改Dialog只有未设置action的时候,内部才会自动关闭,如果有设置action,则关闭时机交给业务自己处理 ([#117](https://github.com/Tencent/tdesign-flutter/issues/117)) + +### 🚧 Others +- 增加国际化语言适配功能 +- 适配3.16后文本居中,增加TDTextConfig使用文档 + + +## 🌈 0.1.4 `2024-04-08` + +### 🚀 Features +- `TDCountDown`: + - add: 新增TDCountDown倒计时组件 @hkaikai +- `TDTheme`: + - add: 修改主题实现方式,支持ref属性进行自定义映射 + - add: 添加默认数字字体 numberFontFamily +- `TDText`: + - add: 添加TDText强制居中开关 kTextForceVerticalCenterEnable,可以全局禁用强制居中,防止flutter 3.16版本之后文字偏移太多([#35](https://github.com/Tencent/tdesign-flutter/issues/35)) +- `TDBottomTabBar`: + - add: 添加自定义背景颜色功能([#55](https://github.com/Tencent/tdesign-flutter/issues/55)) +- `TDCheckbox`: + - add: TDCheckbox和TDRadio支持自定义颜色([#57](https://github.com/Tencent/tdesign-flutter/issues/57)) + - add: TDCheckbox和TDRadio支持自定义字体大小([#66](https://github.com/Tencent/tdesign-flutter/issues/66)) +- `TDTabBar`: + - add: TDTabBar添加分割线的颜色和高度的自定义设置([#71](https://github.com/Tencent/tdesign-flutter/issues/71)) +- `TDSwitch`: + - add: TDSwitch 支持自定义"开/关"文案 ([#73](https://github.com/Tencent/tdesign-flutter/issues/73)) +- `TDDialog`: + - add: 添加自定义标题对齐和内容Widget的功能 ([#58](https://github.com/Tencent/tdesign-flutter/issues/58)) + + +### 🐞 Bug Fixes +- `TDSlider`: + - fix: 修复TDSlider单游标模式下设置showThumbValue不起作用的问题。 +- `TDButton`: + - fix: 修复TDButton外部设置主题颜色不生效的问题 ([#54](https://github.com/Tencent/tdesign-flutter/issues/54)) +- `TDInput`: + - fix: 修复TDInput的showBottomDivider不生效的问题 ([#70](https://github.com/Tencent/tdesign-flutter/issues/70)) + - fix: TDInput去掉无效的height API,使用SizedBox来修改高度 ([#70](https://github.com/Tencent/tdesign-flutter/issues/70)) + +### 🚧 Others +- example应用,添加修改主题按钮,可快速修改主题颜色 + + +## 🌈 0.1.3 `2024-03-15` + +### 🚀 Features +- `TDButton`: + - add:支持通过TDButtonStyle.radius自定义圆角大小 +- `TDPicker`: + - add: picker组件滚动PC支持鼠标拖拽 + - add: TDPicker和TDDatePicker组件,onConfirm内部不在默认pop弹窗组件,允许外部自定义处理;OnCancel不为空时不再自动pop组件 +- `TDSwitch`: + - add: onChanged支持外部指定是否消费事件,如果已消费则内部不再处理([#27](https://github.com/Tencent/tdesign-flutter/issues/27)) +- `TDBottomTabBar`: + - add: 增加自定义标签文字样式,优化labText和icon传递参数([#49](https://github.com/Tencent/tdesign-flutter/issues/49)) + + +### 🐞 Bug Fixes +- `TDNavBar`: + - fix: NavBar顶部高度改为实时获取,防止最开始的时候拿不到([#34](https://github.com/Tencent/tdesign-flutter/issues/34)) +- `TDDialog`: + - fix: DialogInfo 中 contentColor 参数没有传进去 ([#37](https://github.com/Tencent/tdesign-flutter/pull/37)) +- `TDButton`: + - fix: TDButton点击禁用效果无效问题 ([#44](https://github.com/Tencent/tdesign-flutter/issues/44)) +- `TDInput`: + - fix: 删除按钮内部没有自动刷新的问题 ([#30](https://github.com/Tencent/tdesign-flutter/issues/30)) + - fix: 修复输入内容长度和inputFormatters互斥问题 ([#38](https://github.com/Tencent/tdesign-flutter/issues/38)) +- `TDAlertDialog`: + - fix: 组件的默认按钮的操作为开放 ([#40](https://github.com/Tencent/tdesign-flutter/issues/40)) +- `TDRadio`: + - fix: 水平排列会强制添加下划线 ([#40](https://github.com/Tencent/tdesign-flutter/issues/40)) +- `TDTabBar`: + - fix: indicatorColor不生效问题 ([#31](https://github.com/Tencent/tdesign-flutter/issues/31)) + +### 🚧 Others +- 优化了TDButton,TDText,TDTheme等常用组件的性能 + + + +## 🌈 0.1.2 `2024-01-08` + +### 🚀 Features +- `TDImage`: + - add:图片增加FitWidth类型,修改对应Demo页面 ([#14](https://github.com/Tencent/tdesign-flutter/pull/14)) +- `TDLoading`: + - add: 加载添加显示与隐藏的方法 ([#15](https://github.com/Tencent/tdesign-flutter/pull/15)) +- `TDPopup`: + - add: 添加自定义圆角支持 ([#17](https://github.com/Tencent/tdesign-flutter/pull/17)) +- `TDAvatar`: + - add:头像类型为字符、图标时,支持自定义背景颜色 ([#20](https://github.com/Tencent/tdesign-flutter/pull/20)) + +### 🐞 Bug Fixes +- `TDBottomTabBar`: + - 添加安全区域,修复 ([#1](https://github.com/Tencent/tdesign-flutter/issues/1)) +- `TDButton`: + - update widget 可更新按钮disable状态 + - fix: 按钮点击态过短 ([#13](https://github.com/Tencent/tdesign-flutter/pull/13)) +- `TDSwiper`: + - fix: 适配swiper竖向点条状样式 ([#19](https://github.com/Tencent/tdesign-flutter/pull/19)) +- `TDInput`: + - fix: type为TDInputType.twoLine下leftLabelStyle设置不生效 ([#21](https://github.com/Tencent/tdesign-flutter/pull/21)) + +### 🚧 Others +- 修改最低兼容版本为3.7.0 ([#3](https://github.com/Tencent/tdesign-flutter/issues/3)) + +## 0.1.1 + +* 回退代码规范,兼容3.7.x + +## 0.1.0 + +* 发布比较稳定的版本到pub + +## 0.0.9 + +* 修改代码规范 + +## 0.0.8 + +* 更新 License + +## 0.0.7 + +* 修改 example的main.dart + +## 0.0.6 + +* 修改slider组件,使其与flutter sdk 版本解耦 + +## 0.0.5 + +* 发布到pub仓库 + +## 0.0.4 + +* 修复一些已知bug + +## 0.0.3 + +* 删除TDText中相关package的默认值,允许package传null + +## 0.0.2 + +* 更新ReadMe,修改引入文件为'tdesign_flutter.dart' + +## 0.0.1 + +* 正式发布,包含TDButton等29个组件,提供TDTheme、TDIcon等基础属性 diff --git a/tdesign-site/CONTRIBUTING.md b/tdesign-site/CONTRIBUTING.md new file mode 100644 index 000000000..416df63d5 --- /dev/null +++ b/tdesign-site/CONTRIBUTING.md @@ -0,0 +1,201 @@ +--- +title: 贡献指南 +# description: +spline: explain +--- + +## 1.项目地址 +https://github.com/Tencent/tdesign-flutter + +目录结构: +```text +tdesign-component/ +├── demo_tool // API和演示代码工具 +├── examples // 组件使用示例 +├── lib // 组件库 +└── tests // 组件测试 + +tdesign-site/ // tdesign flutter站点 + +tdesign-adaptation/ // tdesign flutter 版本适配仓库 +``` +## 2.如何运行 +### 2.1 Flutter基础知识 +- Flutter基础介绍:[https://book.flutterchina.club/chapter1/flutter_intro.html](https://book.flutterchina.club/chapter1/flutter_intro.html) +- Dart语言介绍:[https://book.flutterchina.club/chapter1/dart.html](https://book.flutterchina.club/chapter1/dart.html) +- 搭建Flutter开发环境:[https://book.flutterchina.club/chapter1/install_flutter.html](https://book.flutterchina.club/chapter1/install_flutter.html) +- 计数器应用示例:[https://book.flutterchina.club/chapter2/first_flutter_app.html](https://book.flutterchina.club/chapter2/first_flutter_app.html) + +### 2.2 开发环境要求 +flutter sdk 版本:3.16.9 + +(注: TD需要支持3.16.9~最新稳定版本,因此最好再3.16.9版本开发完成后,使用最新稳定版本确认能否正常运行,如过版本代码无法兼容,请将不兼容文件移至tdesign-adaptation,做兼容处理) + +### 2.3 克隆项目 +``` +// 克隆项目 +git clone https://github.com/Tencent/tdesign-flutter.git +``` + + +### 2.3 运行flutter项目 +下面是命令行运行方法,正常也可以不使用命令行,而在IDE直接运行,推荐使用Android Studio进行开发。运行时请选择Android设备和iOS模拟器,不要直接在浏览器运行。 + +*注:首次开发请先运行tdesign-flutter/tdesign-component/init.sh脚本,它会根据当前flutter版本,选择适合的tdesign-adaptation依赖* +``` +// 运行flutter组件项目 +cd tdesign-flutter/tdesign-component/ + +// 拉取依赖 +flutter pub get + +// 进入示例项目 需要把AOPMarket|enable 改为false,禁用aop才行 +cd example/ + +// 运行项目,最好在运行前先确认设备已连接 +flutter run +``` + +### 2.4 Flutter多版本兼容 +在flutte 3.32版本,sdk代码变更较大,同一代码可能无法同时支持跨版本运行,因此抽离了tdesign-adaptation库,用于进行不同flutter版本之间的代码适配。 + + +#### 2.4.1 版本切换 +首次运行,可以先将flutter sdk 切到3.16.9版本,执行tdesign-component/init.sh脚本,配置对应依赖。 + +开发完成,切换至最新稳定版尝试运行,如果有不兼容代码,需要在tdesign-adaptation库进行适配。 + +其中,高于3.32版本的代码,请在feature/3.32_adaptation分支开发,低于3.32版本的代码,请在feature/3.16_adaptation分支开发。 + +本地开发tdesign-adaptation,可以修改tdesign-component/pubspec_overrides.yaml和tdesign-component/example/pubspec_overrides.yaml文件,使用本地依赖,内容如下: +```yaml +dependency_overrides: + tdesign_adaptation: + path: ../tdesign-adaptation # 本地相对路径 +``` +但是,提交git时,请不要将pubspec_overrides.yaml提交到仓库!!! + +#### 2.4.2 国际化适配 +由于3.32版本的国际化功能与3.16版本差异比较大,代码生成位置发生变更,无法跨版本兼容。因此,国际化资源代码改为手动依赖方式。如果需要修改字段内容,需要把生成的app_localizations(_en、_zh).dart文件拷贝到example/lib/localizations目录 + + +### 2.5 运行前端官网项目 + +``` +// 进入官网前端项目 +cd tdesign-flutter/tdesign-site/site/ + +// 安装依赖 +npm install + +// 回到tdesign-site目录 +cd .. + +// 运行项目 +npm run site:dev +``` +注:本地运行项目,右侧example未展示对应组件示例,是正常现象,该示例正式部署才会展示。 +
+ +
+ + +## 3.如何领取issue +### 3.1 一个issue的完整流程: +- 用户发现功能不满足,查看API文档确认不支持,然后提issue。[常见问题](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-site/FAQ.md) +- 提了issue后,在群里发出issue链接,方便后续直接沟通,确认详细需求。然后将最终确认结果贴到github的issue上。 +- 负责人评估开发工作量,打`help want`和`issueShoot`标签。 +- 贡献者确认要领取,在issue下评论领取,并通知负责人。负责人打上Received标签,讨论确认实现方案. +- 贡献者参考开发规范开发完功能,按照文档自检,然后提pr,并同步负责人验收。 +- pr自动构建测试包。【自动构建流水线】 +- 负责人拉设计一起视觉走查(如果需要的话),走查完成后,合并pr。issue打上下一个版本的标签。 +- 版本发布后,issue关闭。 + + +### 3.2 标签管理 +- 无标签:未评估 +- issueShoot: 已评工作量,可领取 +- Received:已领取 +- pre_x.x.x:预计某个版本发布 +- x.x.x: 某个版本已发布 + +## 4.开发规范 +- 组件命名规范:以TD为前缀,组件名称、API名称参考TDesign现有组件和API命名,可以根据flutter原生Widget的特点进行修改。组件API以满足设计要求和使用为准,可根据flutter特点做精简或定制。 +- 组件库用到的所有色值、圆角、字体字号等样式属性需全部定义在主题中。 +- 代码规范遵循腾讯Dart代码规范。 +- 对于系统原有组件,如Text,Image等,应兼容系统原组件功能,只能扩展,不能阉割,以免业务需要使用系统功能时,必须放弃TDesign控件。 +- 示例页面尽量使用ExamplePage+ExampleModule+ExampleItem组合,按照示例稿的布局实现;页面写完后,在main.dart中修改exampleMap对应组件的isTodo属性即可。 +- 组件API和演示代码,请参考demo_tool/README.md文件。 +- 组件内部的固定文案,都应该抽离到TDResourceDelegate中统一管理,方便业务进行国际化适配 +- 如果使用的组件TD有封装,尽量使用TD已有组件,而非直接使用系统组件 + + +## 5.验收标准 +### 5.1 pr规则 +- pr目标分支为develop分支,请勿直接往main分支合并 +- 标题格式:`组件类名`: 修改描述(示例:`TDUpload`: 新增Upload组件;`TDBottomTabBar`: 修复iconText模式,底部溢出2.5像素) +- 勾选规则: + > 1.只要有新增参数,就勾选”新特性提交“ + > + > 2.只修改内部bug,未新增参数,才勾选”日常 bug 修复“ + > + > 3.其他选项视具体改动判断 +- ”相关issue“处带上修复的issue链接 +- 其他内容视情况可选填 + + +### 5.2 代码review自检【欢迎大家补充】 +- 尽量使用TD已有组件,而非系统组件。比如有直接使用Text组件的,请换成TDText组件。 +- 检测边界条件,比如参数为null是否会导致代码异常? +- 是否提供验收用例?用例是否ExamplePage放在test字段里? +- 是否提供完善文档? + +### 5.3 文档自检 +#### 5.3.1 API 文档: +- tdesign-component/demo_tool/all_build.sh文件中是否包含对应组件API的脚本,若未包含,请参考tdesign-component/demo_tool/README.md及其他组件示例添加脚本。请确保提交的all_build.sh中触发的是./bin/api_tool_linux,因为github自动打包的环境是linux系统 +- 检测组件属性的注释是否完善 +- 如果组件有对应的系统组件,检测是否有系统组件支持的参数TD组件不支持 +- 检测组件与https://tdesign.tencent.com/mobile-vue/overview 是否有含义相同但命名不同的组件,如果有,尽量保持命名一致。 +- tdesign-component/demo_tool/all_build.sh中的名字与tdesign-component/example/lib/config.dart的名字需保持一致 +
+ +
+ + +#### 5.3.2 新增组件: +- tdesign-site/site/site.config.mjs + > 菜单栏是否放开了注释?如果没有被注释组件,则参考其他组件添加。 + > + > name与tdesign-component/example/lib/config.dart的ExamplePageModel的name参数是否一致 + +
+ +
+- tdesign-site/site/docs/overview.md:检查组件预览是否已添加、分组组件数量是否正确 +
+ +
+ + +### 5.4 demo自测 +提交pr后,会触发流水线自动打包。打包完成会添加评论如下: +
+ +
+点击”体验apk“后面链接,即可跳转打包完成的apk下载地址,可以安装自测。 +注:该demo是基于Flutter 3.16.9打包的。请留意最新Flutter版本的包是否打包成功;如果评论打包失败,请检测失败原因。 + +#### 自测内容: +- 组件改动后,整体是否有明显的布局溢出等展示异常 +- 新增功能是否在”单元测试“栏目下?(需把示例放在ExamplePage的test字段里) +- 验证功能是否符合预期 +- 点击右上角”i“图标,查看API文档是否正确。(若不正确,检测all_build.sh是否配置了对应组件的脚本) +- 点击右上角""图标,检测示例代码是否生成。(若未生成,参考其他有生成的模块修改) +- 如涉及视觉改动,请联系负责人拉取设计同学进行视觉走查 +- 英文环境、切换主题后是否展示正常 + +### 5.5视觉走查 +pr打包完成后,通知负责人拉设计同学进群,将pr链接发到群里。设计同学根据“设计走查流水线地址”的链接,替换内网域名后,下载ipa包进行走查。 + +## 6.合并pr +issue验收完成后,由负责人合并pr,并将issue的标签改为pre_x.x.x diff --git a/tdesign-site/FAQ.md b/tdesign-site/FAQ.md new file mode 100644 index 000000000..ddad78761 --- /dev/null +++ b/tdesign-site/FAQ.md @@ -0,0 +1,62 @@ +--- +title: 常见问题 +description: +spline: explain +--- + +## 版本节奏 +TDesign Flutter 从0.2.0版本开始,正常情况下,每月初发一个版本,若有节假日等特殊情况再特殊处理。 + +新版本携带的功能,可以通过issue的标签查看,带pre_x.x.x标签的issue,则表示已开发完成,预计在x.x.x发布。 + +## 自定义主题 +- 自定义主题用法请参考:https://tdesign.tencent.com/flutter/getting-started#%E8%87%AA%E5%AE%9A%E4%B9%89%E4%B8%BB%E9%A2%98 +- 如果自定义主题未生效,请检查是否设置:TDTheme.needMultiTheme(true); +- 在启动即修改主题颜色,完整示例代码请参考:https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/example/lib/component_test/test_app.dart +- 在应用使用中切换主题颜色,示例代码请参考example的main.dart和home.dart:https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/example/lib/main.dart +- 转换完整代码:https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/example/shell/theme/css2JsonTheme.dart + +## 暗色模式 +TDesignFlutter目前未内置暗色模式,请使用“自定义主题”方式自己适配。 + +## 文字居中 +Flutter官方SDK不支持文本居中功能,若要实现通用的文字居中方案,需要修改Flutter引擎,使用成本较高,因此,TDesign采用的是一个折中方案:针对常用字体,通过在其顶部添加padding的方式,达到居中的效果。 + +默认计算padding的尺寸,以Pixel 4模拟器的默认字体为基准,不同机型的字体不一样,有的机型可能需要的尺寸与默认适配字体差异较大,因此会出现添加padding后,文字更不居中的问题。 + +TDesign Flutter 0.1.4版本开始,添加了全局变量kTextForceVerticalCenterEnable来控制是否使用内部padding,如果将全局变量kTextForceVerticalCenterEnable设为false,则显示效果与直接使用官方Text一致。(部分机型,尤其是iOS机型,将kTextForceVerticalCenterEnable设为false,可能比设为true更居中,遇到文字不居中问题,可以尝试将kTextForceVerticalCenterEnable改为false看看效果。) + +TDesign Flutter 0.1.5版本之后,可以通过重写TDTextPaddingConfig的paddingRate和paddingExtraRate进行自定义适配,TDTextPaddingConfig使用方法可参考TDTextPage。如果kTextForceVerticalCenterEnable设为false也无法满足需求,则可以通过重写TDTextPaddingConfig自定义适配主流机型。 + +## 新增组件 +如果有新增组件的想法,可以提issue,或者在已有issue补充。如果想提交代码,开发实现,可以拉负责人一起评估。 + +## Input相关 +- 自定义高度: TDInput没有自带height参数,可以通过外部嵌套SizeBox来修改高度。不过修改高度后,内部相关高度不会等比缩放,需要业务自己同步修改。 +- 输入正则:Input的FilteringTextInputFormatter.allow(RegExp(r''))的正则是匹配即将输入的单个字符串的,不是匹配已输入的整个字符串的,按字符串匹配写的正则可能导致无法输入。 + +## TDImage缓存问题 +TDImage基于系统Image组件封装,未单独处理缓存逻辑,使用的是系统组件自带的缓存。 + +## Toast 使用context +目前TDToast显示需要context,如果使用的是GetX,可以考虑是否要方法记录一个全局context,再给TDToast使用。如果后续实现方案优化了context,将更新本文档。 + +## 内部写死的颜色或尺寸 +如果发现组件内部写死了颜色或尺寸,导致无法适应业务场景,可以直接提issue优化。 + +## flutter 3.32以下SDK 无法运行 +flutter 3.32版本的sdk代码变更很大,无法跨版本兼容,因此引入了tdesign_flutter_adaptation库。 + +如果你是flutter 3.32以下的sdk版本,请在项目的pubspec.yaml中添加以下依赖覆盖: +```yaml +dependency_overrides: + tdesign_flutter_adaptation: 3.16.0 + image_picker: 1.0.8 +``` + +如果你是tdesign_flutter 0.2.3版本,且使用了3.32版本以上的Flutter SDK,请在项目的pubspec.yaml中添加以下依赖覆盖: +```yaml +dependency_overrides: + tdesign_flutter_adaptation: 3.32.0 + image_picker: 1.1.2 +``` diff --git a/tdesign-site/LICENSE b/tdesign-site/LICENSE new file mode 100644 index 000000000..789cbde93 --- /dev/null +++ b/tdesign-site/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2021-present TDesign + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tdesign-site/README.md b/tdesign-site/README.md new file mode 100644 index 000000000..c08f6e95f --- /dev/null +++ b/tdesign-site/README.md @@ -0,0 +1,42 @@ + +[TDesign](https://github.com/Tencent/tdesign) 适配Flutter的组件库。 + +## 预览 + +
+Android请扫码下载预览 ↓ +
+ +
+iOS请运行项目预览 ↓ +
+https://github.com/Tencent/tdesign-flutter/tree/main/tdesign-component + +## 使用组件 + +### yaml引入依赖 + +```yaml + dependencies: + tdesign_flutter: ^0.1.0 +``` + + +### 引入组件代码 + +```dart +import 'package:tdesign_flutter/tdesign_flutter.dart'; +``` + +## 本地运行官网示例代码 + +- 进入 `site` 文件夹,安装`npm install` 安装依赖包 +- `cd ..` 回到 `tdesign-site` 目录,运行命令 `npm run site:dev` + +## 基础库版本 + +最低基础库版本`^0.1.0` + +## 开源协议 + +TDesign 遵循 [MIT 协议](https://github.com/Tencent/tdesign-flutter/blob/main/tdesign-component/LICENSE)。 diff --git a/tdesign-site/USED.md b/tdesign-site/USED.md new file mode 100644 index 000000000..8ffd85385 --- /dev/null +++ b/tdesign-site/USED.md @@ -0,0 +1,11 @@ +--- +title: 谁在用 +# description: +spline: explain +--- + +## 上线项目案例 + +目前已在心悦俱乐部上线稳定运行 + +![tgclub](used_tgclub.png) \ No newline at end of file diff --git a/tdesign-site/babel.config.js b/tdesign-site/babel.config.js new file mode 100644 index 000000000..dd4e3d695 --- /dev/null +++ b/tdesign-site/babel.config.js @@ -0,0 +1,8 @@ +// babel.config.js +module.exports = { + presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript'], + plugins: [ + ['@babel/plugin-proposal-decorators', { legacy: true }], + ['@babel/plugin-proposal-class-properties', { loose: true }], + ], +}; diff --git a/tdesign-site/commitlint.config.js b/tdesign-site/commitlint.config.js new file mode 100644 index 000000000..422b19445 --- /dev/null +++ b/tdesign-site/commitlint.config.js @@ -0,0 +1 @@ +module.exports = { extends: ['@commitlint/config-conventional'] }; diff --git a/tdesign-site/jest.config.js b/tdesign-site/jest.config.js new file mode 100644 index 000000000..ebd09a712 --- /dev/null +++ b/tdesign-site/jest.config.js @@ -0,0 +1,31 @@ +module.exports = { + verbose: true, + testEnvironment: 'jsdom', + testURL: 'http://localhost/', + moduleFileExtensions: ['js', 'ts'], + moduleNameMapper: { + '^tdesign-flutter/(.*)': '/src/$1', + }, + testMatch: ['/src/**/__test__/**/*.test.{js,ts}'], + collectCoverageFrom: ['/src/**/*.{js,ts}', '!**/__test__/**', '!**/_example/**'], + collectCoverage: true, + coverageProvider: 'v8', + coverageDirectory: '/test/unit/coverage', + reporters: [ + 'default', + [ + './node_modules/jest-html-reporter', + { + pageTitle: 'TDesign-flutter Unit Test Report', + outputPath: './test/unit/report/test-report.html', + }, + ], + ], + setupFiles: ['/script/test/setup.js'], + coverageReporters: ['html', 'json-summary'], + globals: { + CONFIG_PREFIX: 't', + }, + globalSetup: '/script/test/globalSetup.js', + snapshotSerializers: ['flutter-simulate/jest-snapshot-plugin'], +}; diff --git a/tdesign-site/jest.e2e.config.js b/tdesign-site/jest.e2e.config.js new file mode 100644 index 000000000..03d538957 --- /dev/null +++ b/tdesign-site/jest.e2e.config.js @@ -0,0 +1,18 @@ +module.exports = { + verbose: true, + testEnvironment: 'jsdom', + testURL: 'http://localhost/', + moduleFileExtensions: ['js', 'ts'], + testMatch: ['/example/**/__test__/**/*.test.{js,ts}'], + collectCoverageFrom: ['/example/**/*.{js,ts}', '!**/__test__/**'], + collectCoverage: true, + coverageDirectory: '/test/e2e/coverage', + reporters: [ + 'default', + ['./node_modules/jest-html-reporter', { + pageTitle: 'TDesign-flutter E2E Test Report', + outputPath: './test/e2e/report/test-report.html', + }], + ], + coverageReporters: ['html', 'text-summary'], +}; diff --git a/tdesign-site/jest.virtualHost.config.js b/tdesign-site/jest.virtualHost.config.js new file mode 100644 index 000000000..cd942a930 --- /dev/null +++ b/tdesign-site/jest.virtualHost.config.js @@ -0,0 +1,37 @@ +/** + * virtualHost 的开启和关闭对 dom 渲染会产生较大差异,且在单测中无法通过 selectComponent 获取子组件实例, + * 故设置两个 jest 配置文件,区分 virtualHost 的两种状态。 + */ + +module.exports = { + verbose: true, + testEnvironment: 'jsdom', + testURL: 'http://localhost/', + moduleFileExtensions: ['js', 'ts'], + moduleNameMapper: { + '^tdesign-flutter/(.*)': '/src/$1', + }, + testMatch: ['/src/**/__test__/**/*.test.{js,ts}'], + collectCoverageFrom: ['/src/**/*.{js,ts}', '!**/__test__/**', '!**/_example/**'], + collectCoverage: true, + coverageProvider: 'v8', + coverageDirectory: '/test/unit-virtualHost/coverage', + reporters: [ + 'default', + [ + './node_modules/jest-html-reporter', + { + pageTitle: 'TDesign-flutter Unit Test Report(virtualHost)', + outputPath: './test/unit-virtualHost/report/test-report.html', + }, + ], + ], + setupFiles: ['/script/test/virtualHostSetup.js'], + coverageReporters: ['html', 'json-summary'], + globals: { + CONFIG_PREFIX: 't', + }, + globalSetup: '/script/test/globalSetup.js', + snapshotSerializers: ['flutter-simulate/jest-snapshot-plugin'], + snapshotResolver: '/script/test/snapshotResolver.js', +}; diff --git a/tdesign-site/package-lock.json b/tdesign-site/package-lock.json new file mode 100644 index 000000000..a1004a38b --- /dev/null +++ b/tdesign-site/package-lock.json @@ -0,0 +1,24465 @@ +{ + "name": "tdesign-flutter", + "version": "0.1.2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tdesign-flutter", + "version": "0.1.2", + "license": "MIT", + "dependencies": { + "dayjs": "^1.10.7", + "tdesign-flutter": "file:" + }, + "devDependencies": { + "@babel/core": "^7.12.10", + "@babel/plugin-proposal-decorators": "^7.18.9", + "@babel/preset-env": "^7.12.11", + "@babel/preset-typescript": "^7.12.7", + "@commitlint/cli": "^16.0.2", + "@commitlint/config-conventional": "^16.0.0", + "@rollup/plugin-node-resolve": "^13.0.5", + "@types/jest": "^27.0.3", + "@typescript-eslint/eslint-plugin": "^5.6.0", + "@typescript-eslint/parser": "~5.35.0", + "@vitejs/plugin-vue": "^2.3.3", + "@vitejs/plugin-vue-jsx": "^1.1.7", + "@vue/compiler-sfc": "^3.2.4", + "axios": "^1.1.3", + "babel-jest": "^26.6.3", + "commitizen": "^4.2.4", + "cross-env": "^7.0.2", + "cz-conventional-changelog": "^3.3.0", + "del": "^6.1.1", + "eslint": "^7.0.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-import": "^2.24.2", + "eslint-plugin-prettier": "^4.0.0", + "glob": "^8.1.0", + "gray-matter": "^4.0.3", + "gulp": "^4.0.2", + "gulp-changed": "^4.0.2", + "gulp-if": "^3.0.0", + "gulp-less": "^5.0.0", + "gulp-mp-npm": "^1.9.7", + "gulp-plumber": "^1.2.1", + "gulp-rename": "^2.0.0", + "gulp-replace": "^1.0.0", + "gulp-replace-task": "^2.0.1", + "gulp-sourcemaps": "^3.0.0", + "gulp-typescript": "^6.0.0-alpha.1", + "husky": "^7.0.4", + "jest": "^26.6.3", + "jest-html-reporter": "^3.3.0", + "jsdom": "^20.0.0", + "less": "^4.1.1", + "lint-staged": "^10.0.0-1", + "lodash": "^4.17.21", + "miniprogram-api-typings": "^3.4.6", + "miniprogram-automator": "^0.10.0", + "miniprogram-simulate": "^1.5.7", + "npm-run-all": "^4.1.5", + "playwright": "^1.19.1", + "prettier": "^2.0.5", + "prismjs": "^1.24.1", + "standard-changelog": "^2.0.27", + "stylelint": "^13.13.1", + "tdesign-icons-view": "^0.1.0", + "tdesign-publish-cli": "^0.0.12", + "tdesign-site-components": "^0.13.18", + "typescript": "~4.7.2", + "vite": "^2.7.6", + "vite-plugin-tdoc": "^2.0.1", + "vue": "^3.2.4", + "vue-router": "^4.0.11" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", + "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.21.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.7.tgz", + "integrity": "sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.21.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz", + "integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.5", + "@babel/helper-compilation-targets": "^7.21.5", + "@babel/helper-module-transforms": "^7.21.5", + "@babel/helpers": "^7.21.5", + "@babel/parser": "^7.21.8", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.5", + "@babel/types": "^7.21.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.5.tgz", + "integrity": "sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz", + "integrity": "sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz", + "integrity": "sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.21.5", + "@babel/helper-validator-option": "^7.21.0", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.21.8", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.8.tgz", + "integrity": "sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.21.5", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-member-expression-to-functions": "^7.21.5", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.21.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/helper-split-export-declaration": "^7.18.6", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.21.8", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.8.tgz", + "integrity": "sha512-zGuSdedkFtsFHGbexAvNuipg1hbtitDLo2XE8/uf6Y9sOQV1xsYX/2pNbtedp/X0eU1pIt+kGvaqHCowkRbS5g==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.3.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz", + "integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz", + "integrity": "sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", + "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz", + "integrity": "sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.21.5", + "@babel/helper-module-imports": "^7.21.4", + "@babel/helper-simple-access": "^7.21.5", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.5", + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", + "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.21.5.tgz", + "integrity": "sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.21.5", + "@babel/helper-member-expression-to-functions": "^7.21.5", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.5", + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", + "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", + "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", + "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.5.tgz", + "integrity": "sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.5", + "@babel/types": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.21.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz", + "integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", + "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", + "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.21.0.tgz", + "integrity": "sha512-MfgX49uRrFUTL/HvWtmx3zmpyzMMr4MTj3d527MLlr/4RTT9G/ytFFP7qet2uM2Ve03b+BkpWUpK+lRXnQ+v9w==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/plugin-syntax-decorators": "^7.21.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz", + "integrity": "sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.21.0.tgz", + "integrity": "sha512-tIoPpGBR8UuM4++ccWN3gifhVvQu7ZizuR1fklhRJrd5ewgbkUS+0KVFeWWxELtn18NTLoW32XV7zyOgIAiz+w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", + "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.21.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", + "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz", + "integrity": "sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", + "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz", + "integrity": "sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz", + "integrity": "sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz", + "integrity": "sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/template": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz", + "integrity": "sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz", + "integrity": "sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", + "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz", + "integrity": "sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.21.5", + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-simple-access": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", + "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-identifier": "^7.19.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz", + "integrity": "sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz", + "integrity": "sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "regenerator-transform": "^0.15.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", + "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.21.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.3.tgz", + "integrity": "sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-typescript": "^7.20.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz", + "integrity": "sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.21.5.tgz", + "integrity": "sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.21.5", + "@babel/helper-compilation-targets": "^7.21.5", + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.20.7", + "@babel/plugin-proposal-async-generator-functions": "^7.20.7", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.21.0", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.20.7", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.7", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.21.0", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.21.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.21.5", + "@babel/plugin-transform-async-to-generator": "^7.20.7", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.21.0", + "@babel/plugin-transform-classes": "^7.21.0", + "@babel/plugin-transform-computed-properties": "^7.21.5", + "@babel/plugin-transform-destructuring": "^7.21.3", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.21.5", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.20.11", + "@babel/plugin-transform-modules-commonjs": "^7.21.5", + "@babel/plugin-transform-modules-systemjs": "^7.20.11", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.20.5", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.21.3", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.21.5", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.20.7", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.21.5", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.21.5", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-typescript": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.21.5.tgz", + "integrity": "sha512-iqe3sETat5EOrORXiQ6rWfoOg2y68Cs75B9wNxdPW4kixJxh7aXQE1KPdWLDniC24T/6dSnguF33W9j/ZZQcmA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.21.5", + "@babel/helper-validator-option": "^7.21.0", + "@babel/plugin-syntax-jsx": "^7.21.4", + "@babel/plugin-transform-modules-commonjs": "^7.21.5", + "@babel/plugin-transform-typescript": "^7.21.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, + "node_modules/@babel/runtime": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", + "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz", + "integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.21.4", + "@babel/generator": "^7.21.5", + "@babel/helper-environment-visitor": "^7.21.5", + "@babel/helper-function-name": "^7.21.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.21.5", + "@babel/types": "^7.21.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.21.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz", + "integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.21.5", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@cnakazawa/watch": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", + "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "dev": true, + "dependencies": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + }, + "bin": { + "watch": "cli.js" + }, + "engines": { + "node": ">=0.1.95" + } + }, + "node_modules/@commitlint/cli": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-16.3.0.tgz", + "integrity": "sha512-P+kvONlfsuTMnxSwWE1H+ZcPMY3STFaHb2kAacsqoIkNx66O0T7sTpBxpxkMrFPyhkJiLJnJWMhk4bbvYD3BMA==", + "dev": true, + "dependencies": { + "@commitlint/format": "^16.2.1", + "@commitlint/lint": "^16.2.4", + "@commitlint/load": "^16.3.0", + "@commitlint/read": "^16.2.1", + "@commitlint/types": "^16.2.1", + "lodash": "^4.17.19", + "resolve-from": "5.0.0", + "resolve-global": "1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-16.2.4.tgz", + "integrity": "sha512-av2UQJa3CuE5P0dzxj/o/B9XVALqYzEViHrMXtDrW9iuflrqCStWBAioijppj9URyz6ONpohJKAtSdgAOE0gkA==", + "dev": true, + "dependencies": { + "conventional-changelog-conventionalcommits": "^4.3.1" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-16.2.1.tgz", + "integrity": "sha512-hogSe0WGg7CKmp4IfNbdNES3Rq3UEI4XRPB8JL4EPgo/ORq5nrGTVzxJh78omibNuB8Ho4501Czb1Er1MoDWpw==", + "dev": true, + "dependencies": { + "@commitlint/types": "^16.2.1", + "ajv": "^6.12.6" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/ensure": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-16.2.1.tgz", + "integrity": "sha512-/h+lBTgf1r5fhbDNHOViLuej38i3rZqTQnBTk+xEg+ehOwQDXUuissQ5GsYXXqI5uGy+261ew++sT4EA3uBJ+A==", + "dev": true, + "dependencies": { + "@commitlint/types": "^16.2.1", + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-16.2.1.tgz", + "integrity": "sha512-oSls82fmUTLM6cl5V3epdVo4gHhbmBFvCvQGHBRdQ50H/690Uq1Dyd7hXMuKITCIdcnr9umyDkr8r5C6HZDF3g==", + "dev": true, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/format": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-16.2.1.tgz", + "integrity": "sha512-Yyio9bdHWmNDRlEJrxHKglamIk3d6hC0NkEUW6Ti6ipEh2g0BAhy8Od6t4vLhdZRa1I2n+gY13foy+tUgk0i1Q==", + "dev": true, + "dependencies": { + "@commitlint/types": "^16.2.1", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/format/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@commitlint/format/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/format/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@commitlint/format/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@commitlint/format/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/format/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-16.2.4.tgz", + "integrity": "sha512-Lxdq9aOAYCOOOjKi58ulbwK/oBiiKz+7Sq0+/SpFIEFwhHkIVugvDvWjh2VRBXmRC/x5lNcjDcYEwS/uYUvlYQ==", + "dev": true, + "dependencies": { + "@commitlint/types": "^16.2.1", + "semver": "7.3.7" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/is-ignored/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@commitlint/is-ignored/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@commitlint/is-ignored/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@commitlint/lint": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-16.2.4.tgz", + "integrity": "sha512-AUDuwOxb2eGqsXbTMON3imUGkc1jRdtXrbbohiLSCSk3jFVXgJLTMaEcr39pR00N8nE9uZ+V2sYaiILByZVmxQ==", + "dev": true, + "dependencies": { + "@commitlint/is-ignored": "^16.2.4", + "@commitlint/parse": "^16.2.1", + "@commitlint/rules": "^16.2.4", + "@commitlint/types": "^16.2.1" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/load": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-16.3.0.tgz", + "integrity": "sha512-3tykjV/iwbkv2FU9DG+NZ/JqmP0Nm3b7aDwgCNQhhKV5P74JAuByULkafnhn+zsFGypG1qMtI5u+BZoa9APm0A==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^16.2.1", + "@commitlint/execute-rule": "^16.2.1", + "@commitlint/resolve-extends": "^16.2.1", + "@commitlint/types": "^16.2.1", + "@types/node": ">=12", + "chalk": "^4.0.0", + "cosmiconfig": "^7.0.0", + "cosmiconfig-typescript-loader": "^2.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0", + "typescript": "^4.4.3" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/load/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@commitlint/load/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/load/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@commitlint/load/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@commitlint/load/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/load/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/message": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-16.2.1.tgz", + "integrity": "sha512-2eWX/47rftViYg7a3axYDdrgwKv32mxbycBJT6OQY/MJM7SUfYNYYvbMFOQFaA4xIVZt7t2Alyqslbl6blVwWw==", + "dev": true, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/parse": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-16.2.1.tgz", + "integrity": "sha512-2NP2dDQNL378VZYioLrgGVZhWdnJO4nAxQl5LXwYb08nEcN+cgxHN1dJV8OLJ5uxlGJtDeR8UZZ1mnQ1gSAD/g==", + "dev": true, + "dependencies": { + "@commitlint/types": "^16.2.1", + "conventional-changelog-angular": "^5.0.11", + "conventional-commits-parser": "^3.2.2" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/read": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-16.2.1.tgz", + "integrity": "sha512-tViXGuaxLTrw2r7PiYMQOFA2fueZxnnt0lkOWqKyxT+n2XdEMGYcI9ID5ndJKXnfPGPppD0w/IItKsIXlZ+alw==", + "dev": true, + "dependencies": { + "@commitlint/top-level": "^16.2.1", + "@commitlint/types": "^16.2.1", + "fs-extra": "^10.0.0", + "git-raw-commits": "^2.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-16.2.1.tgz", + "integrity": "sha512-NbbCMPKTFf2J805kwfP9EO+vV+XvnaHRcBy6ud5dF35dxMsvdJqke54W3XazXF1ZAxC4a3LBy4i/GNVBAthsEg==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^16.2.1", + "@commitlint/types": "^16.2.1", + "import-fresh": "^3.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0", + "resolve-global": "^1.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/rules": { + "version": "16.2.4", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-16.2.4.tgz", + "integrity": "sha512-rK5rNBIN2ZQNQK+I6trRPK3dWa0MtaTN4xnwOma1qxa4d5wQMQJtScwTZjTJeallFxhOgbNOgr48AMHkdounVg==", + "dev": true, + "dependencies": { + "@commitlint/ensure": "^16.2.1", + "@commitlint/message": "^16.2.1", + "@commitlint/to-lines": "^16.2.1", + "@commitlint/types": "^16.2.1", + "execa": "^5.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-16.2.1.tgz", + "integrity": "sha512-9/VjpYj5j1QeY3eiog1zQWY6axsdWAc0AonUUfyZ7B0MVcRI0R56YsHAfzF6uK/g/WwPZaoe4Lb1QCyDVnpVaQ==", + "dev": true, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/top-level": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-16.2.1.tgz", + "integrity": "sha512-lS6GSieHW9y6ePL73ied71Z9bOKyK+Ib9hTkRsB8oZFAyQZcyRwq2w6nIa6Fngir1QW51oKzzaXfJL94qwImyw==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/types": { + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-16.2.1.tgz", + "integrity": "sha512-7/z7pA7BM0i8XvMSBynO7xsB3mVQPUZbVn6zMIlp/a091XJ3qAXRXc+HwLYhiIdzzS5fuxxNIHZMGHVD4HJxdA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@commitlint/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@commitlint/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", + "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@github/clipboard-copy-element": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@github/clipboard-copy-element/-/clipboard-copy-element-1.1.2.tgz", + "integrity": "sha512-L6CMrcA5we0udafvoSuRCE/Ci/3xrLWKYRGup2IlhxF771bQYsQ2EB1of182pI8ZWM4oxgwzu37+igMeoZjN/A==", + "dev": true + }, + "node_modules/@gulp-sourcemaps/identity-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-2.0.1.tgz", + "integrity": "sha512-Tb+nSISZku+eQ4X1lAkevcQa+jknn/OVUgZ3XCxEKIsLsqYuPoJwJOPQeaOk75X3WPftb29GWY1eqE7GLsXb1Q==", + "dev": true, + "dependencies": { + "acorn": "^6.4.1", + "normalize-path": "^3.0.0", + "postcss": "^7.0.16", + "source-map": "^0.6.0", + "through2": "^3.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha512-o/EatdaGt8+x2qpb0vFLC/2Gug/xYPRXb6a+ET1wGYKozKN3krDWC/zZFZAtrzxJHuDL12mwdfEFKcKMNvc55A==", + "dev": true, + "dependencies": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/@gulp-sourcemaps/map-sources/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@gulp-sourcemaps/map-sources/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/@gulp-sourcemaps/map-sources/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/@gulp-sourcemaps/map-sources/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/@gulp-sourcemaps/map-sources/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@hutson/parse-repository-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", + "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", + "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^26.6.2", + "jest-util": "^26.6.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/console/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/console/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/console/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", + "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/reporters": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-changed-files": "^26.6.2", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-resolve-dependencies": "^26.6.3", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "jest-watcher": "^26.6.2", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/core/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", + "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/fake-timers": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", + "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@sinonjs/fake-timers": "^6.0.1", + "@types/node": "*", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/globals": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", + "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/types": "^26.6.2", + "expect": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/reporters": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", + "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.2", + "graceful-fs": "^4.2.4", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^4.0.3", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.2", + "jest-haste-map": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "slash": "^3.0.0", + "source-map": "^0.6.0", + "string-length": "^4.0.1", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^7.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "optionalDependencies": { + "node-notifier": "^8.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/reporters/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", + "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.7.5", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", + "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0", + "graceful-fs": "^4.2.4", + "source-map": "^0.6.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/test-result": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", + "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", + "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-runner": "^26.6.3", + "jest-runtime": "^26.6.3" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/transform": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", + "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/types": "^26.6.2", + "babel-plugin-istanbul": "^6.0.0", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-haste-map": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-util": "^26.6.2", + "micromatch": "^4.0.2", + "pirates": "^4.0.1", + "slash": "^3.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/transform/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/transform/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/transform/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jimp/bmp": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.6.8.tgz", + "integrity": "sha512-uxVgSkI62uAzk5ZazYHEHBehow590WAkLKmDXLzkr/XP/Hv2Fx1T4DKwJ/15IY5ktq5VAhAUWGXTyd8KWFsx7w==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "bmp-js": "^0.1.0", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/core": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.6.8.tgz", + "integrity": "sha512-JOFqBBcSNiDiMZJFr6OJqC6viXj5NVBQISua0eacoYvo4YJtTajOIxC4MqWyUmGrDpRMZBR8QhSsIOwsFrdROA==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "any-base": "^1.1.0", + "buffer": "^5.2.0", + "core-js": "^2.5.7", + "exif-parser": "^0.1.12", + "file-type": "^9.0.0", + "load-bmfont": "^1.3.1", + "mkdirp": "0.5.1", + "phin": "^2.9.1", + "pixelmatch": "^4.0.2", + "tinycolor2": "^1.4.1" + } + }, + "node_modules/@jimp/core/node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", + "dev": true + }, + "node_modules/@jimp/core/node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dev": true, + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/@jimp/custom": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.6.8.tgz", + "integrity": "sha512-FrYlzZRVXP2vuVwd7Nc2dlK+iZk4g6IaT1Ib8Z6vU5Kkwlt83FJIPJ2UUFABf3bF5big0wkk8ZUihWxE4Nzdng==", + "dev": true, + "dependencies": { + "@jimp/core": "^0.6.8", + "core-js": "^2.5.7" + } + }, + "node_modules/@jimp/gif": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.6.8.tgz", + "integrity": "sha512-yyOlujjQcgz9zkjM5ihZDEppn9d1brJ7jQHP5rAKmqep0G7FU1D0AKcV+Ql18RhuI/CgWs10wAVcrQpmLnu4Yw==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7", + "omggif": "^1.0.9" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/jpeg": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.6.8.tgz", + "integrity": "sha512-rGtXbYpFXAn471qLpTGvhbBMNHJo5KiufN+vC5AWyufntmkt5f0Ox2Cx4ijuBMDtirZchxbMLtrfGjznS4L/ew==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7", + "jpeg-js": "^0.3.4" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-blit": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.6.8.tgz", + "integrity": "sha512-7Tl6YpKTSpvwQbnGNhsfX2zyl3jRVVopd276Y2hF2zpDz9Bycow7NdfNU/4Nx1jaf96X6uWOtSVINcQ7rGd47w==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-blur": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.6.8.tgz", + "integrity": "sha512-NpZCMKxXHLDQsX9zPlWtpMA660DQStY6/z8ZetyxCDbqrLe9YCXpeR4MNhdJdABIiwTm1W5FyFF4kp81PHJx3Q==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-color": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.6.8.tgz", + "integrity": "sha512-jjFyU0zNmGOH2rjzHuOMU4kaia0oo82s/7UYfn5h7OUkmUZTd6Do3ZSK1PiXA7KR+s4B76/Omm6Doh/0SGb7BQ==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7", + "tinycolor2": "^1.4.1" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-contain": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.6.8.tgz", + "integrity": "sha512-p/P2wCXhAzbmEgXvGsvmxLmbz45feF6VpR4m9suPSOr8PC/i/XvTklTqYEUidYYAft4vHgsYJdS74HKSMnH8lw==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-blit": ">=0.3.5", + "@jimp/plugin-resize": ">=0.3.5", + "@jimp/plugin-scale": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-cover": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.6.8.tgz", + "integrity": "sha512-2PvWgk+PJfRsfWDI1G8Fpjrsu0ZlpNyZxO2+fqWlVo6y/y2gP4v08FqvbkcqSjNlOu2IDWIFXpgyU0sTINWZLg==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-crop": ">=0.3.5", + "@jimp/plugin-resize": ">=0.3.5", + "@jimp/plugin-scale": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-crop": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.6.8.tgz", + "integrity": "sha512-CbrcpWE2xxPK1n/JoTXzhRUhP4mO07mTWaSavenCg664oQl/9XCtL+A0FekuNHzIvn4myEqvkiTwN7FsbunS/Q==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-displace": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.6.8.tgz", + "integrity": "sha512-RmV2bPxoPE6mrPxtYSPtHxm2cGwBQr5a2p+9gH6SPy+eUMrbGjbvjwKNfXWUYD0leML+Pt5XOmAS9pIROmuruQ==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-dither": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.6.8.tgz", + "integrity": "sha512-x6V/qjxe+xypjpQm7GbiMNqci1EW5UizrcebOhHr8AHijOEqHd2hjXh5f6QIGfrkTFelc4/jzq1UyCsYntqz9Q==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-flip": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.6.8.tgz", + "integrity": "sha512-4il6Da6G39s9MyWBEee4jztEOUGJ40E6OlPjkMrdpDNvge6hYEAB31BczTYBP/CEY74j4LDSoY5LbcU4kv06yA==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-rotate": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-gaussian": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.6.8.tgz", + "integrity": "sha512-pVOblmjv7stZjsqloi4YzHVwAPXKGdNaHPhp4KP4vj41qtc6Hxd9z/+VWGYRTunMFac84gUToe0UKIXd6GhoKw==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-invert": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.6.8.tgz", + "integrity": "sha512-11zuLiXDHr6tFv4U8aieXqNXQEKbDbSBG/h+X62gGTNFpyn8EVPpncHhOqrAFtZUaPibBqMFlNJ15SzwC7ExsQ==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-mask": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.6.8.tgz", + "integrity": "sha512-hZJ0OiKGJyv7hDSATwJDkunB1Ie80xJnONMgpUuUseteK45YeYNBOiZVUe8vum8QI1UwavgBzcvQ9u4fcgXc9g==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-normalize": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.6.8.tgz", + "integrity": "sha512-Q4oYhU+sSyTJI7pMZlg9/mYh68ujLfOxXzQGEXuw0sHGoGQs3B0Jw7jmzGa6pIS06Hup5hD2Zuh1ppvMdjJBfQ==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-print": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.6.8.tgz", + "integrity": "sha512-2aokejGn4Drv1FesnZGqh5KEq0FQtR0drlmtyZrBH+r9cx7hh0Qgf4D1BOTDEgXkfSSngjGRjKKRW/fwOrVXYw==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7", + "load-bmfont": "^1.4.0" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-blit": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-resize": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.6.8.tgz", + "integrity": "sha512-27nPh8L1YWsxtfmV/+Ub5dOTpXyC0HMF2cu52RQSCYxr+Lm1+23dJF70AF1poUbUe+FWXphwuUxQzjBJza9UoA==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-rotate": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.6.8.tgz", + "integrity": "sha512-GbjETvL05BDoLdszNUV4Y0yLkHf177MnqGqilA113LIvx9aD0FtUopGXYfRGVvmtTOTouoaGJUc+K6qngvKxww==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-blit": ">=0.3.5", + "@jimp/plugin-crop": ">=0.3.5", + "@jimp/plugin-resize": ">=0.3.5" + } + }, + "node_modules/@jimp/plugin-scale": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.6.8.tgz", + "integrity": "sha512-GzIYWR/oCUK2jAwku23zt19V1ssaEU4pL0x2XsLNKuuJEU6DvEytJyTMXCE7OLG/MpDBQcQclJKHgiyQm5gIOQ==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5", + "@jimp/plugin-resize": ">=0.3.5" + } + }, + "node_modules/@jimp/plugins": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.6.8.tgz", + "integrity": "sha512-fMcTI72Vn/Lz6JftezTURmyP5ml/xGMe0Ljx2KRJ85IWyP33vDmGIUuutFiBEbh2+y7lRT+aTSmjs0QGa/xTmQ==", + "dev": true, + "dependencies": { + "@jimp/plugin-blit": "^0.6.8", + "@jimp/plugin-blur": "^0.6.8", + "@jimp/plugin-color": "^0.6.8", + "@jimp/plugin-contain": "^0.6.8", + "@jimp/plugin-cover": "^0.6.8", + "@jimp/plugin-crop": "^0.6.8", + "@jimp/plugin-displace": "^0.6.8", + "@jimp/plugin-dither": "^0.6.8", + "@jimp/plugin-flip": "^0.6.8", + "@jimp/plugin-gaussian": "^0.6.8", + "@jimp/plugin-invert": "^0.6.8", + "@jimp/plugin-mask": "^0.6.8", + "@jimp/plugin-normalize": "^0.6.8", + "@jimp/plugin-print": "^0.6.8", + "@jimp/plugin-resize": "^0.6.8", + "@jimp/plugin-rotate": "^0.6.8", + "@jimp/plugin-scale": "^0.6.8", + "core-js": "^2.5.7", + "timm": "^1.6.1" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/png": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.6.8.tgz", + "integrity": "sha512-JHHg/BZ7KDtHQrcG+a7fztw45rdf7okL/YwkN4qU5FH7Fcrp41nX5QnRviDtD9hN+GaNC7kvjvcqRAxW25qjew==", + "dev": true, + "dependencies": { + "@jimp/utils": "^0.6.8", + "core-js": "^2.5.7", + "pngjs": "^3.3.3" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/tiff": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.6.8.tgz", + "integrity": "sha512-iWHbxd+0IKWdJyJ0HhoJCGYmtjPBOusz1z1HT/DnpePs/Lo3TO4d9ALXqYfUkyG74ZK5jULZ69KLtwuhuJz1bg==", + "dev": true, + "dependencies": { + "core-js": "^2.5.7", + "utif": "^2.0.1" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/types": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.6.8.tgz", + "integrity": "sha512-vCZ/Cp2osy69VP21XOBACfHI5HeR60Rfd4Jidj4W73UL+HrFWOtyQiJ7hlToyu1vI5mR/NsUQpzyQvz56ADm5A==", + "dev": true, + "dependencies": { + "@jimp/bmp": "^0.6.8", + "@jimp/gif": "^0.6.8", + "@jimp/jpeg": "^0.6.8", + "@jimp/png": "^0.6.8", + "@jimp/tiff": "^0.6.8", + "core-js": "^2.5.7", + "timm": "^1.6.1" + }, + "peerDependencies": { + "@jimp/custom": ">=0.3.5" + } + }, + "node_modules/@jimp/utils": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.6.8.tgz", + "integrity": "sha512-7RDfxQ2C/rarNG9iso5vmnKQbcvlQjBIlF/p7/uYj72WeZgVCB+5t1fFBKJSU4WhniHX4jUMijK+wYGE3Y3bGw==", + "dev": true, + "dependencies": { + "core-js": "^2.5.7" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", + "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz", + "integrity": "sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "deepmerge": "^4.2.2", + "is-builtin-module": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "rollup": "^2.42.0" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "dependencies": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", + "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-5.3.1.tgz", + "integrity": "sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.6.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "node_modules/@stylelint/postcss-css-in-js": { + "version": "0.37.3", + "resolved": "https://registry.npmjs.org/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.3.tgz", + "integrity": "sha512-scLk3cSH1H9KggSniseb2KNAU5D9FWc3H7BxCSAIdtU9OWIyw0zkEZ9qEKHryRM+SExYXRKNb7tOOVNAsQ3iwg==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "dependencies": { + "@babel/core": "^7.17.9" + }, + "peerDependencies": { + "postcss": ">=7.0.0", + "postcss-syntax": ">=0.36.2" + } + }, + "node_modules/@stylelint/postcss-markdown": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/@stylelint/postcss-markdown/-/postcss-markdown-0.36.2.tgz", + "integrity": "sha512-2kGbqUVJUGE8dM+bMzXG/PYUWKkjLIkRLWNh39OaADkiabDRdw8ATFCgbMz5xdIcvwspPAluSL7uY+ZiTWdWmQ==", + "deprecated": "Use the original unforked package instead: postcss-markdown", + "dev": true, + "dependencies": { + "remark": "^13.0.0", + "unist-util-find-all-after": "^3.0.2" + }, + "peerDependencies": { + "postcss": ">=7.0.0", + "postcss-syntax": ">=0.36.2" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", + "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.18.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.5.tgz", + "integrity": "sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "node_modules/@types/expect": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", + "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", + "dev": true + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "27.5.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz", + "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", + "dev": true, + "dependencies": { + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/linkify-it": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", + "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", + "dev": true, + "peer": true + }, + "node_modules/@types/markdown-it": { + "version": "12.2.3", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", + "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "dev": true, + "peer": true, + "dependencies": { + "@types/linkify-it": "*", + "@types/mdurl": "*" + } + }, + "node_modules/@types/mdast": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", + "integrity": "sha512-Y/uImid8aAwrEA24/1tcRZwpxX3pIFTSilcNDKSPn+Y2iDywSEachzRuvgAYYLR3wpGXAsMbv5lvKLDZLeYPAw==", + "dev": true, + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/mdurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", + "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", + "dev": true, + "peer": true + }, + "node_modules/@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.1.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.1.4.tgz", + "integrity": "sha512-At4pvmIOki8yuwLtd7BNHl3CiWNbtclUbNtScGx4OHfBd4/oWoJC8KRCIxXwkdndzhxOsPXihrsOoydxBjlE9Q==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", + "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "dev": true + }, + "node_modules/@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", + "dev": true + }, + "node_modules/@types/vinyl": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.7.tgz", + "integrity": "sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg==", + "dev": true, + "dependencies": { + "@types/expect": "^1.20.4", + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "15.0.15", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.15.tgz", + "integrity": "sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.5.tgz", + "integrity": "sha512-feA9xbVRWJZor+AnLNAr7A8JRWeZqHUf4T9tlP+TN04b05pFVhO5eN7/O93Y/1OUlLMHKbnJisgDURs/qvtqdg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.5", + "@typescript-eslint/type-utils": "5.59.5", + "@typescript-eslint/utils": "5.59.5", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.35.1.tgz", + "integrity": "sha512-XL2TBTSrh3yWAsMYpKseBYTVpvudNf69rPOWXWVBI08My2JVT5jR66eTt4IgQFHA/giiKJW5dUD4x/ZviCKyGg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.35.1", + "@typescript-eslint/types": "5.35.1", + "@typescript-eslint/typescript-estree": "5.35.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { + "version": "5.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.35.1.tgz", + "integrity": "sha512-kCYRSAzIW9ByEIzmzGHE50NGAvAP3wFTaZevgWva7GpquDyFPFcmvVkFJGWJJktg/hLwmys/FZwqM9EKr2u24Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.35.1", + "@typescript-eslint/visitor-keys": "5.35.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { + "version": "5.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.35.1.tgz", + "integrity": "sha512-FDaujtsH07VHzG0gQ6NDkVVhi1+rhq0qEvzHdJAQjysN+LHDCKDKCBRlZFFE0ec0jKxiv0hN63SNfExy0KrbQQ==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.35.1.tgz", + "integrity": "sha512-JUqE1+VRTGyoXlDWWjm6MdfpBYVq+hixytrv1oyjYIBEOZhBCwtpp5ZSvBt4wIA1MKWlnaC2UXl2XmYGC3BoQA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.35.1", + "@typescript-eslint/visitor-keys": "5.35.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.35.1.tgz", + "integrity": "sha512-cEB1DvBVo1bxbW/S5axbGPE6b7FIMAbo3w+AGq6zNDA7+NYJOIkKj/sInfTv4edxd4PxJSgdN4t6/pbvgA+n5g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.35.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/parser/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.5.tgz", + "integrity": "sha512-jVecWwnkX6ZgutF+DovbBJirZcAxgxC0EOHYt/niMROf8p4PwxxG32Qdhj/iIQQIuOflLjNkxoXyArkcIP7C3A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.5", + "@typescript-eslint/visitor-keys": "5.59.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.5.tgz", + "integrity": "sha512-4eyhS7oGym67/pSxA2mmNq7X164oqDYNnZCUayBwJZIRVvKpBCMBzFnFxjeoDeShjtO6RQBHBuwybuX3POnDqg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.59.5", + "@typescript-eslint/utils": "5.59.5", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.5.tgz", + "integrity": "sha512-xkfRPHbqSH4Ggx4eHRIO/eGL8XL4Ysb4woL8c87YuAo8Md7AUjyWKa9YMwTL519SyDPrfEgKdewjkxNCVeJW7w==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.5.tgz", + "integrity": "sha512-+XXdLN2CZLZcD/mO7mQtJMvCkzRfmODbeSKuMY/yXbGkzvA9rJyDY5qDYNoiz2kP/dmyAxXquL2BvLQLJFPQIg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.5", + "@typescript-eslint/visitor-keys": "5.59.5", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.5.tgz", + "integrity": "sha512-sCEHOiw+RbyTii9c3/qN74hYDPNORb8yWCoPLmB7BIflhplJ65u2PBpdRla12e3SSTJ2erRkPjz7ngLHhUegxA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.5", + "@typescript-eslint/types": "5.59.5", + "@typescript-eslint/typescript-estree": "5.59.5", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.59.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.5.tgz", + "integrity": "sha512-qL+Oz+dbeBRTeyJTIy0eniD3uvqU7x+y1QceBismZ41hd4aBSRh8UAw4pZP0+XzLuPZmx4raNMq/I+59W2lXKA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.5", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-vue": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-2.3.4.tgz", + "integrity": "sha512-IfFNbtkbIm36O9KB8QodlwwYvTEsJb4Lll4c2IwB3VHc2gie2mSPtSzL0eYay7X2jd/2WX02FjSGTWR6OPr/zg==", + "dev": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "vite": "^2.5.10", + "vue": "^3.2.25" + } + }, + "node_modules/@vitejs/plugin-vue-jsx": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-1.3.10.tgz", + "integrity": "sha512-Cf5zznh4yNMiEMBfTOztaDVDmK1XXfgxClzOSUVUc8WAmHzogrCUeM8B05ABzuGtg0D1amfng+mUmSIOFGP3Pw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.17.9", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-transform-typescript": "^7.16.8", + "@rollup/pluginutils": "^4.2.0", + "@vue/babel-plugin-jsx": "^1.1.1", + "hash-sum": "^2.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@vitejs/plugin-vue-jsx/node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/@vitejs/plugin-vue-jsx/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.2.tgz", + "integrity": "sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==", + "dev": true + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.1.tgz", + "integrity": "sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.0.0", + "@babel/template": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "@vue/babel-helper-vue-transform-on": "^1.0.2", + "camelcase": "^6.0.0", + "html-tags": "^3.1.0", + "svg-tags": "^1.0.0" + } + }, + "node_modules/@vue/babel-plugin-jsx/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.3.2.tgz", + "integrity": "sha512-CKZWo1dzsQYTNTft7whzjL0HsrEpMfiK7pjZ2WFE3bC1NA7caUjWioHSK+49y/LK7Bsm4poJZzAMnvZMQ7OTeg==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.21.3", + "@vue/shared": "3.3.2", + "estree-walker": "^2.0.2", + "source-map-js": "^1.0.2" + } + }, + "node_modules/@vue/compiler-core/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/@vue/compiler-dom": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.3.2.tgz", + "integrity": "sha512-6gS3auANuKXLw0XH6QxkWqyPYPunziS2xb6VRenM3JY7gVfZcJvkCBHkb5RuNY1FCbBO3lkIi0CdXUCW1c7SXw==", + "dev": true, + "dependencies": { + "@vue/compiler-core": "3.3.2", + "@vue/shared": "3.3.2" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.3.2.tgz", + "integrity": "sha512-jG4jQy28H4BqzEKsQqqW65BZgmo3vzdLHTBjF+35RwtDdlFE+Fk1VWJYUnDMMqkFBo6Ye1ltSKVOMPgkzYj7SQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.15", + "@vue/compiler-core": "3.3.2", + "@vue/compiler-dom": "3.3.2", + "@vue/compiler-ssr": "3.3.2", + "@vue/reactivity-transform": "3.3.2", + "@vue/shared": "3.3.2", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.0", + "postcss": "^8.1.10", + "source-map-js": "^1.0.2" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.3.2.tgz", + "integrity": "sha512-K8OfY5FQtZaSOJHHe8xhEfIfLrefL/Y9frv4k4NsyQL3+0lRKxr9QuJhfdBDjkl7Fhz8CzKh63mULvmOfx3l2w==", + "dev": true, + "dependencies": { + "@vue/compiler-dom": "3.3.2", + "@vue/shared": "3.3.2" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.0.tgz", + "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==", + "dev": true + }, + "node_modules/@vue/reactivity": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.3.2.tgz", + "integrity": "sha512-yX8C4uTgg2Tdj+512EEMnMKbLveoITl7YdQX35AYgx8vBvQGszKiiCN46g4RY6/deeo/5DLbeUUGxCq1qWMf5g==", + "dev": true, + "dependencies": { + "@vue/shared": "3.3.2" + } + }, + "node_modules/@vue/reactivity-transform": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.3.2.tgz", + "integrity": "sha512-iu2WaQvlJHdnONrsyv4ibIEnSsuKF+aHFngGj/y1lwpHQtalpVhKg9wsKMoiKXS9zPNjG9mNKzJS9vudvjzvyg==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.15", + "@vue/compiler-core": "3.3.2", + "@vue/shared": "3.3.2", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.0" + } + }, + "node_modules/@vue/reactivity-transform/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/@vue/runtime-core": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.3.2.tgz", + "integrity": "sha512-qSl95qj0BvKfcsO+hICqFEoLhJn6++HtsPxmTkkadFbuhe3uQfJ8HmQwvEr7xbxBd2rcJB6XOJg7nWAn/ymC5A==", + "dev": true, + "dependencies": { + "@vue/reactivity": "3.3.2", + "@vue/shared": "3.3.2" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.3.2.tgz", + "integrity": "sha512-+drStsJT+0mtgHdarT7cXZReCcTFfm6ptxMrz0kAW5hms6UNBd8Q1pi4JKlncAhu+Ld/TevsSp7pqAZxBBoGng==", + "dev": true, + "dependencies": { + "@vue/runtime-core": "3.3.2", + "@vue/shared": "3.3.2", + "csstype": "^3.1.1" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.3.2.tgz", + "integrity": "sha512-QCwh6OGwJg6GDLE0fbQhRTR6tnU+XDJ1iCsTYHXBiezCXAhqMygFRij7BiLF4ytvvHcg5kX9joX5R5vP85++wg==", + "dev": true, + "dependencies": { + "@vue/compiler-ssr": "3.3.2", + "@vue/shared": "3.3.2" + }, + "peerDependencies": { + "vue": "3.3.2" + } + }, + "node_modules/@vue/shared": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.3.2.tgz", + "integrity": "sha512-0rFu3h8JbclbnvvKrs7Fe5FNGV9/5X2rPD7KmOzhLSUAiQH5//Hq437Gv0fR5Mev3u/nbtvmLl8XgwCU20/ZfQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", + "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "dev": true, + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals/node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", + "dev": true + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha512-eCjan3AVo/SxZ0/MyIYRtkpxIu/H3xZN7URr1vXVrISxeyz8fUFz0FJziamK4sS8I+t35y4rHg1b2PklyBe/7A==", + "dev": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "dev": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha512-ewaIr5y+9CUTGFwZfpECUbFlGcC0GCw1oqR9RI6h1gQCd9Aj2GxSckCnPsVJnmfMZbwFYE+leZGASgkWl06Jow==", + "dev": true, + "dependencies": { + "ansi-wrap": "0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha512-ZyznvL8k/FZeQHr2T6LzcJ/+vBApDnMNZvfVFy3At0knswWd6rJ3/0Hhmpu8oqa6C92npmozs890sX9Dl6q+Qw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/any-base": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", + "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha512-WLbYiXzD3y/ATLZFufV/rZvWdZOs+Z/+5v1rBZ463Jn398pa6kcde27cvozYnBoxXblGZTFfoPpsaEw0orU5BA==", + "dev": true, + "dependencies": { + "buffer-equal": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/applause": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/applause/-/applause-2.0.4.tgz", + "integrity": "sha512-wFhNjSoflbAEgelX3psyKSXV2iQFjuYW31DEhcCOD/bQ98VdfltLclK4p1mI6E58Qp4Q7+5RCbBdr+Nc9b5QhA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21", + "optional-require": "^1.0.2" + }, + "engines": { + "node": ">=10" + }, + "optionalDependencies": { + "cson-parser": "^4.0.8", + "js-yaml": "^4.0.0" + } + }, + "node_modules/applause/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "optional": true + }, + "node_modules/applause/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "optional": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", + "dev": true + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA==", + "dev": true, + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw==", + "dev": true, + "dependencies": { + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha512-BC4Yl89vneCYfpLrs5JU2aAu9/a+xWbeKhvISg9PT7eWFB9UlRvI+rKEtk6mgxWr3dSkk9gQ8hCrdqt06NXPdw==", + "dev": true, + "dependencies": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-initial/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "dev": true, + "dependencies": { + "is-number": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-last/node_modules/is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "dev": true, + "dependencies": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-sort/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.reduce": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz", + "integrity": "sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "node_modules/assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ast-module-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-3.0.0.tgz", + "integrity": "sha512-CMxMCOCS+4D+DkOQfuZf+vLrSEmY/7xtORwdxs4wtcC1wVgvk2MqFFTwQCFhvWsI4KPU9lcWXPI8DgRiz+xetQ==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/async-each": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz", + "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "dev": true + }, + "node_modules/async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha512-VPXfB4Vk49z1LHHodrEQ6Xf7W4gg1w0dAPROHngx7qgDjqmIQ+fXmwgGXTW/ITLai0YLSvWepJOP9EVpMnEAcw==", + "dev": true, + "dependencies": { + "async-done": "^1.2.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "dependencies": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + }, + "node_modules/autoprefixer/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/autoprefixer/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/babel-jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", + "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", + "dev": true, + "dependencies": { + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/babel__core": "^7.1.7", + "babel-plugin-istanbul": "^6.0.0", + "babel-preset-jest": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/babel-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", + "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.0.0", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", + "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^26.6.2", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": ">= 10.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha512-bZOOfCb3gXBXbTFXq3OZtGR88LwGeJvzu6szttaIzymOTS4ZttBNOWSv7aLZja2EMycKtRYV0Oa8SNKH/zkxvg==", + "dev": true, + "dependencies": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "dependencies": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/binaryextensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz", + "integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==", + "dev": true, + "engines": { + "node": ">=0.8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", + "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", + "dev": true + }, + "node_modules/browserslist": { + "version": "4.21.5", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", + "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001449", + "electron-to-chromium": "^1.4.284", + "node-releases": "^2.0.8", + "update-browserslist-db": "^1.0.10" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "dev": true, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "dependencies": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001487", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001487.tgz", + "integrity": "sha512-83564Z3yWGqXsh2vaH/mhXfEM0wX+NlBCm1jYHOb97TrTWJEmPTccZgeLTPBUUb0PNVo+oomb7wkimZBIERClA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "dependencies": { + "rsvp": "^4.8.4" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "optionalDependencies": { + "fsevents": "^1.2.7" + } + }, + "node_modules/chokidar/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/chokidar/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + }, + "engines": { + "node": ">= 4.0" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/chokidar/node_modules/glob-parent/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chokidar/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", + "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", + "dev": true + }, + "node_modules/class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/class-utils/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", + "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cli-truncate/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/cli-truncate/node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/clone-regexp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", + "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", + "dev": true, + "dependencies": { + "is-regexp": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-regexp/node_modules/is-regexp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", + "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", + "dev": true + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/cloneable-readable/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/cloneable-readable/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/cloneable-readable/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/coffeescript": { + "version": "1.12.7", + "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.12.7.tgz", + "integrity": "sha512-pLXHFxQMPklVoEekowk8b3erNynC+DVJzChxS/LCBBgR6/8AJkHivkm//zbowcfc7BTCAjryuhx6gPqPRfsFoA==", + "dev": true, + "optional": true, + "bin": { + "cake": "bin/cake", + "coffee": "bin/coffee" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha512-5D2XXSpkOnleOI21TG7p3T0bGAsZ/XknZpKBmGYyluO8pw4zA3K8ZlrBIbC4FXg3m6z/RNFiUFfT2sQK01+UHA==", + "dev": true, + "dependencies": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "dev": true, + "dependencies": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/commitizen": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.3.0.tgz", + "integrity": "sha512-H0iNtClNEhT0fotHvGV3E9tDejDeS04sN1veIebsKYGMuGscFaswRoYJKmT3eW85eIJAs0F28bG2+a/9wCOfPw==", + "dev": true, + "dependencies": { + "cachedir": "2.3.0", + "cz-conventional-changelog": "3.3.0", + "dedent": "0.7.0", + "detect-indent": "6.1.0", + "find-node-modules": "^2.1.2", + "find-root": "1.1.0", + "fs-extra": "9.1.0", + "glob": "7.2.3", + "inquirer": "8.2.5", + "is-utf8": "^0.2.1", + "lodash": "4.17.21", + "minimist": "1.2.7", + "strip-bom": "4.0.0", + "strip-json-comments": "3.1.1" + }, + "bin": { + "commitizen": "bin/commitizen", + "cz": "bin/git-cz", + "git-cz": "bin/git-cz" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/commitizen/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/commitizen/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/commitizen/node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, + "node_modules/conventional-changelog-angular": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", + "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.3.tgz", + "integrity": "sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-core": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz", + "integrity": "sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==", + "dev": true, + "dependencies": { + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^5.0.0", + "conventional-commits-parser": "^3.2.0", + "dateformat": "^3.0.0", + "get-pkg-repo": "^4.0.0", + "git-raw-commits": "^2.0.8", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^4.1.1", + "lodash": "^4.17.15", + "normalize-package-data": "^3.0.0", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "through2": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-core/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/conventional-changelog-core/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "dev": true, + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-core/node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/conventional-changelog-core/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/conventional-changelog-core/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", + "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", + "dev": true, + "dependencies": { + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commit-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz", + "integrity": "sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==", + "dev": true + }, + "node_modules/conventional-commits-filter": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "dev": true, + "dependencies": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", + "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "dev": true, + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/copy-props": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz", + "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==", + "dev": true, + "dependencies": { + "each-props": "^1.3.2", + "is-plain-object": "^5.0.0" + } + }, + "node_modules/copy-props/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==", + "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.", + "dev": true, + "hasInstallScript": true + }, + "node_modules/core-js-compat": { + "version": "3.30.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.30.2.tgz", + "integrity": "sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-2.0.2.tgz", + "integrity": "sha512-KmE+bMjWMXJbkWCeY4FJX/npHuZPNr9XF9q9CIQ/bpFwi1qHfCmSiKarrCcRa0LO4fWjk93pVoeRtJAkTGcYNw==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7", + "ts-node": "^10.8.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=7", + "typescript": ">=3" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cson-parser": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/cson-parser/-/cson-parser-4.0.9.tgz", + "integrity": "sha512-I79SAcCYquWnEfXYj8hBqOOWKj6eH6zX1hhX3yqmS4K3bYp7jME3UFpHPzu3rUew0oyfc0s8T6IlWGXRAheHag==", + "dev": true, + "optional": true, + "dependencies": { + "coffeescript": "1.12.7" + }, + "engines": { + "node": ">=10.13" + } + }, + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, + "node_modules/css-tree": { + "version": "1.0.0-alpha.29", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.29.tgz", + "integrity": "sha512-sRNb1XydwkW9IOci6iB2xmy8IGCj6r/fr+JWitvJ2JxQRPzN3T4AGGVWCMlVmVwM1gtgALJRmGIlWv5ppnGGkg==", + "dev": true, + "dependencies": { + "mdn-data": "~1.1.0", + "source-map": "^0.5.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/css/node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csso": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/csso/-/csso-3.5.1.tgz", + "integrity": "sha512-vrqULLffYU1Q2tLdJvaCYbONStnfkfimRxXNaGjxMldI0C7JPBC4rB1RyjhfdZ4m1frm8pM9uRPKH3d2knZ8gg==", + "dev": true, + "dependencies": { + "css-tree": "1.0.0-alpha.29" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", + "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "dev": true + }, + "node_modules/cz-conventional-changelog": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz", + "integrity": "sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "commitizen": "^4.0.3", + "conventional-commit-types": "^3.0.0", + "lodash.map": "^4.5.1", + "longest": "^2.0.1", + "word-wrap": "^1.0.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@commitlint/load": ">6.1.1" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/data-urls": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", + "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/dateformat": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.2.tgz", + "integrity": "sha512-EelsCzH0gMC2YmXuMeaZ3c6md1sUJQxyb1XXc4xaisi/K6qKukqZhKPrEQyRkdNIncgYyLoDTReq0nNyuKerTg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/dayjs": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", + "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug-fabulous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "dev": true, + "dependencies": { + "debug": "3.X", + "memoizee": "0.4.X", + "object-assign": "4.X" + } + }, + "node_modules/debug-fabulous/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "dev": true, + "dependencies": { + "kind-of": "^5.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-compare/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha512-2xaP6GiwVwOEbXCGoJ4ufgC76m8cj805jrghScewJC2ZDsb9U0b4BIrba+xt/Uytyd0HvQ6+WymSRTfnYj59GQ==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-property/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", + "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", + "dev": true, + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha512-CwffZFvlJffUg9zZA0uqrjQayUTC8ob94pnr5sFwaVv3IOmkfUHcWH+jXaQK3askE51Cqe8/9Ql/0uXNwqZ8Zg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detective-amd": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/detective-amd/-/detective-amd-3.1.2.tgz", + "integrity": "sha512-jffU26dyqJ37JHR/o44La6CxtrDf3Rt9tvd2IbImJYxWKTMdBjctp37qoZ6ZcY80RHg+kzWz4bXn39e4P7cctQ==", + "dev": true, + "dependencies": { + "ast-module-types": "^3.0.0", + "escodegen": "^2.0.0", + "get-amd-module-type": "^3.0.0", + "node-source-walk": "^4.2.0" + }, + "bin": { + "detective-amd": "bin/cli.js" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/detective-cjs": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/detective-cjs/-/detective-cjs-3.1.3.tgz", + "integrity": "sha512-ljs7P0Yj9MK64B7G0eNl0ThWSYjhAaSYy+fQcpzaKalYl/UoQBOzOeLCSFEY1qEBhziZ3w7l46KG/nH+s+L7BQ==", + "dev": true, + "dependencies": { + "ast-module-types": "^3.0.0", + "node-source-walk": "^4.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/detective-es6": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-2.2.2.tgz", + "integrity": "sha512-eZUKCUsbHm8xoeoCM0z6JFwvDfJ5Ww5HANo+jPR7AzkFpW9Mun3t/TqIF2jjeWa2TFbAiGaWESykf2OQp3oeMw==", + "dev": true, + "dependencies": { + "node-source-walk": "^4.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/detective-less": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/detective-less/-/detective-less-1.0.2.tgz", + "integrity": "sha512-Rps1xDkEEBSq3kLdsdnHZL1x2S4NGDcbrjmd4q+PykK5aJwDdP5MBgrJw1Xo+kyUHuv3JEzPqxr+Dj9ryeDRTA==", + "dev": true, + "dependencies": { + "debug": "^4.0.0", + "gonzales-pe": "^4.2.3", + "node-source-walk": "^4.0.0" + }, + "engines": { + "node": ">= 6.0" + } + }, + "node_modules/detective-postcss": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detective-postcss/-/detective-postcss-4.0.0.tgz", + "integrity": "sha512-Fwc/g9VcrowODIAeKRWZfVA/EufxYL7XfuqJQFroBKGikKX83d2G7NFw6kDlSYGG3LNQIyVa+eWv1mqre+v4+A==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "is-url": "^1.2.4", + "postcss": "^8.1.7", + "postcss-values-parser": "^2.0.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/detective-sass": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-3.0.2.tgz", + "integrity": "sha512-DNVYbaSlmti/eztFGSfBw4nZvwsTaVXEQ4NsT/uFckxhJrNRFUh24d76KzoCC3aarvpZP9m8sC2L1XbLej4F7g==", + "dev": true, + "dependencies": { + "gonzales-pe": "^4.3.0", + "node-source-walk": "^4.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/detective-scss": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-2.0.2.tgz", + "integrity": "sha512-hDWnWh/l0tht/7JQltumpVea/inmkBaanJUcXRB9kEEXVwVUMuZd6z7eusQ6GcBFrfifu3pX/XPyD7StjbAiBg==", + "dev": true, + "dependencies": { + "gonzales-pe": "^4.3.0", + "node-source-walk": "^4.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/detective-stylus": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detective-stylus/-/detective-stylus-1.0.3.tgz", + "integrity": "sha512-4/bfIU5kqjwugymoxLXXLltzQNeQfxGoLm2eIaqtnkWxqbhap9puDVpJPVDx96hnptdERzS5Cy6p9N8/08A69Q==", + "dev": true + }, + "node_modules/detective-typescript": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-7.0.2.tgz", + "integrity": "sha512-unqovnhxzvkCz3m1/W4QW4qGsvXCU06aU2BAm8tkza+xLnp9SOFnob2QsTxUv5PdnQKfDvWcv9YeOeFckWejwA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "^4.33.0", + "ast-module-types": "^2.7.1", + "node-source-walk": "^4.2.0", + "typescript": "^3.9.10" + }, + "engines": { + "node": "^10.13 || >=12.0.0" + } + }, + "node_modules/detective-typescript/node_modules/@typescript-eslint/types": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", + "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "dev": true, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/detective-typescript/node_modules/@typescript-eslint/typescript-estree": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", + "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "@typescript-eslint/visitor-keys": "4.33.0", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/detective-typescript/node_modules/@typescript-eslint/visitor-keys": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", + "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "4.33.0", + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/detective-typescript/node_modules/ast-module-types": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/ast-module-types/-/ast-module-types-2.7.1.tgz", + "integrity": "sha512-Rnnx/4Dus6fn7fTqdeLEAn5vUll5w7/vts0RN608yFa6si/rDOUonlIIiwugHBFWjylHjxm9owoSZn71KwG4gw==", + "dev": true + }, + "node_modules/detective-typescript/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/detective-typescript/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/detective-typescript/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/detective-typescript/node_modules/typescript": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", + "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/detective-typescript/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diacritics": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz", + "integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/dijkstrajs": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", + "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/dom-serializer/node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "dev": true + }, + "node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "node_modules/domexception": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", + "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "dev": true, + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", + "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "dependencies": { + "domelementtype": "1" + } + }, + "node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexify/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/duplexify/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.394", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.394.tgz", + "integrity": "sha512-0IbC2cfr8w5LxTz+nmn2cJTGafsK9iauV2r5A5scfzyovqLrxuLoxOHE5OBobP3oVIggJT+0JfKnw9sm87c8Hw==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", + "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/encode-utf8": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/encode-utf8/-/encode-utf8-1.0.3.tgz", + "integrity": "sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==", + "dev": true + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.21.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", + "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.2.0", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.7", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", + "dev": true + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/esbuild": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz", + "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/linux-loong64": "0.14.54", + "esbuild-android-64": "0.14.54", + "esbuild-android-arm64": "0.14.54", + "esbuild-darwin-64": "0.14.54", + "esbuild-darwin-arm64": "0.14.54", + "esbuild-freebsd-64": "0.14.54", + "esbuild-freebsd-arm64": "0.14.54", + "esbuild-linux-32": "0.14.54", + "esbuild-linux-64": "0.14.54", + "esbuild-linux-arm": "0.14.54", + "esbuild-linux-arm64": "0.14.54", + "esbuild-linux-mips64le": "0.14.54", + "esbuild-linux-ppc64le": "0.14.54", + "esbuild-linux-riscv64": "0.14.54", + "esbuild-linux-s390x": "0.14.54", + "esbuild-netbsd-64": "0.14.54", + "esbuild-openbsd-64": "0.14.54", + "esbuild-sunos-64": "0.14.54", + "esbuild-windows-32": "0.14.54", + "esbuild-windows-64": "0.14.54", + "esbuild-windows-arm64": "0.14.54" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz", + "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz", + "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz", + "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz", + "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz", + "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz", + "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz", + "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz", + "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz", + "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz", + "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz", + "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz", + "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz", + "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz", + "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz", + "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz", + "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz", + "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz", + "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz", + "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz", + "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/escodegen/node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-airbnb-base": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz", + "integrity": "sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.2" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "eslint": "^5.16.0 || ^6.8.0 || ^7.2.0", + "eslint-plugin-import": "^2.22.1" + } + }, + "node_modules/eslint-config-prettier": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz", + "integrity": "sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==", + "dev": true, + "dependencies": { + "get-stdin": "^6.0.0" + }, + "bin": { + "eslint-config-prettier-check": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=3.14.1" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", + "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "eslint": ">=7.28.0", + "prettier": ">=2.0.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/exec-sh": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", + "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", + "dev": true + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", + "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", + "dev": true, + "dependencies": { + "clone-regexp": "^2.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/exif-parser": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", + "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", + "dev": true + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", + "dev": true, + "dependencies": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/expand-brackets/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-brackets/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", + "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-styles": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-regex-util": "^26.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/expect/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/expect/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/expect/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/expect/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/expect/node_modules/diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/expect/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/expect/node_modules/jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/expect/node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/expect/node_modules/jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/expect/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/expect/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/expr-parser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/expr-parser/-/expr-parser-1.0.0.tgz", + "integrity": "sha512-ncuWTCWH0M5KbaYikXxZ3FG3Q+FTYIEXeXAbxYscdZLFNnR5Le5gRU2r/a/JUZHnxwBDZcxWEWzCoPQlW9Engg==", + "dev": true + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", + "dev": true, + "dependencies": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend-shallow/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "dependencies": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extglob/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "node_modules/fast-fifo": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.2.0.tgz", + "integrity": "sha512-NcvQXt7Cky1cNau15FWy64IjuO8X0JijhTBBrJj1YlxlDfRkJXNaK9RFUjwpfDPzMdv7wB38jr53l9tkNLxnWg==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-type": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-9.0.0.tgz", + "integrity": "sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-node-modules": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz", + "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==", + "dev": true, + "dependencies": { + "findup-sync": "^4.0.0", + "merge": "^2.1.1" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/findup-sync": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", + "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^4.0.2", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==", + "deprecated": "flatten is deprecated in favor of utility frameworks such as lodash.", + "dev": true + }, + "node_modules/flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "node_modules/flush-write-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/flush-write-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/flush-write-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", + "dev": true, + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fork-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", + "integrity": "sha512-Pqq5NnT78ehvUnAk/We/Jr22vSvanRlFTpAmQ88xBY/M1TlHe+P0ILuEyXS595ysdGfaj22634LBkGMA2GTcpA==", + "dev": true + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "dev": true, + "dependencies": { + "map-cache": "^0.2.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fs-access": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz", + "integrity": "sha512-05cXDIwNbFaoFWaz5gNHlUTbH5whiss/hr/ibzPd4MH3cR4w0ZKeIPiVdbyJurg3O5r/Bjpvn9KOb1/rPMf3nA==", + "dev": true, + "dependencies": { + "null-check": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha512-+vSd9frUnapVC2RZYfL3FCB2p3g4TBhaUmrsWlSudsGdnxIuUvBB2QM1VZeBtc49QFwrp+wQLrDs3+xxDgI5gQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/fs-mkdirp-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/fs-mkdirp-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/fs-mkdirp-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/fs-mkdirp-stream/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-amd-module-type": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-amd-module-type/-/get-amd-module-type-3.0.2.tgz", + "integrity": "sha512-PcuKwB8ouJnKuAPn6Hk3UtdfKoUV3zXRqVEvj8XGIXqjWfgd1j7QGdXy5Z9OdQfzVt1Sk29HVe/P+X74ccOuqw==", + "dev": true, + "dependencies": { + "ast-module-types": "^3.0.0", + "node-source-walk": "^4.2.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-pkg-repo": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", + "integrity": "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==", + "dev": true, + "dependencies": { + "@hutson/parse-repository-url": "^3.0.0", + "hosted-git-info": "^4.0.0", + "through2": "^2.0.0", + "yargs": "^16.2.0" + }, + "bin": { + "get-pkg-repo": "src/cli.js" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-pkg-repo/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/get-pkg-repo/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/get-pkg-repo/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/get-pkg-repo/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/get-pkg-repo/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/get-pkg-repo/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/git-raw-commits": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", + "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "dev": true, + "dependencies": { + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", + "dev": true, + "dependencies": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/git-semver-tags": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", + "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", + "dev": true, + "dependencies": { + "meow": "^8.0.0", + "semver": "^6.0.0" + }, + "bin": { + "git-semver-tags": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.2" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha512-uMbLGAP3S2aDOHUDfdoYcdIePUCfysbAd0IAoWVZbeGU/oNQ8asHVSshLDJUPWxfzj8zsCG7/XeHPHTtow0nsw==", + "dev": true, + "dependencies": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glob-stream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-stream/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "dependencies": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + } + }, + "node_modules/glob-stream/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/glob-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/glob-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/glob-watcher": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", + "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", + "dev": true, + "dependencies": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "normalize-path": "^3.0.0", + "object.defaults": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/glob-watcher/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/glob-watcher/node_modules/anymatch/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob-watcher/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "dev": true, + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, + "node_modules/global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "dev": true, + "dependencies": { + "ini": "^1.3.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true + }, + "node_modules/glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "dev": true, + "dependencies": { + "sparkles": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gonzales-pe": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", + "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "gonzales": "bin/gonzales.js" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dev": true, + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true, + "optional": true + }, + "node_modules/gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "dev": true, + "dependencies": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-changed": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gulp-changed/-/gulp-changed-4.0.3.tgz", + "integrity": "sha512-oIymgTNmmIvdqRRpdtohmELix81q+CA/D9DgVCvaM4Ulai0xgalf+XS6A95JwskbxRGQKtzzhMmdWZEuikX67w==", + "dev": true, + "dependencies": { + "make-dir": "^3.0.0", + "plugin-error": "^1.0.1", + "replace-ext": "^1.0.0", + "through2": "^3.0.1", + "touch": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "gulp": ">=4" + }, + "peerDependenciesMeta": { + "gulp": { + "optional": true + } + } + }, + "node_modules/gulp-changed/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/gulp-if": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-if/-/gulp-if-3.0.0.tgz", + "integrity": "sha512-fCUEngzNiEZEK2YuPm+sdMpO6ukb8+/qzbGfJBXyNOXz85bCG7yBI+pPSl+N90d7gnLvMsarthsAImx0qy7BAw==", + "dev": true, + "dependencies": { + "gulp-match": "^1.1.0", + "ternary-stream": "^3.0.0", + "through2": "^3.0.1" + } + }, + "node_modules/gulp-if/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/gulp-less": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-5.0.0.tgz", + "integrity": "sha512-W2I3TewO/By6UZsM/wJG3pyK5M6J0NYmJAAhwYXQHR+38S0iDtZasmUgFCH3CQj+pQYw/PAIzxvFvwtEXz1HhQ==", + "dev": true, + "dependencies": { + "less": "^3.7.1 || ^4.0.0", + "object-assign": "^4.0.1", + "plugin-error": "^1.0.0", + "replace-ext": "^2.0.0", + "through2": "^4.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/gulp-less/node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/gulp-match": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/gulp-match/-/gulp-match-1.1.0.tgz", + "integrity": "sha512-DlyVxa1Gj24DitY2OjEsS+X6tDpretuxD6wTfhXE/Rw2hweqc1f6D/XtsJmoiCwLWfXgR87W9ozEityPCVzGtQ==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.3" + } + }, + "node_modules/gulp-mp-npm": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/gulp-mp-npm/-/gulp-mp-npm-1.10.4.tgz", + "integrity": "sha512-ary+1tUGJ6Y7BNcnlq95B6LKbfcVRWHWtEJC+BIjmIFgBOo7EHKe28vFF24PdnPnzoPnQdhenxxVsBrjkzPbzw==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "fancy-log": "^1.3.3", + "import-regex": "^1.1.0", + "lead": "^3.0.0", + "load-json-file": "^6.2.0 <7.0.0", + "lodash": "^4.17.21", + "precinct": "^8.2.0", + "pumpify": "^2.0.1", + "read-package-tree": "^5.3.1", + "resolve": "^1.20.0", + "through2": "^4.0.2", + "tree-to-list": "^2.0.0", + "vinyl-fs": "^3.0.3", + "xml-parser": "^1.2.1" + }, + "peerDependencies": { + "gulp": "^4.0.2" + } + }, + "node_modules/gulp-mp-npm/node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/gulp-mp-npm/node_modules/lead": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-3.0.0.tgz", + "integrity": "sha512-EOg+EPkppLq8Hm/6zNd3vDPvN3en8R8B1DSPMXwn/assiOpT6yrX8tRXy//9yoTxb0Ph+tGurG5kC98WcNcDhg==", + "dev": true, + "dependencies": { + "streamx": "^2.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/gulp-mp-npm/node_modules/load-json-file": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz", + "integrity": "sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.15", + "parse-json": "^5.0.0", + "strip-bom": "^4.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gulp-mp-npm/node_modules/pumpify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", + "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", + "dev": true, + "dependencies": { + "duplexify": "^4.1.1", + "inherits": "^2.0.3", + "pump": "^3.0.0" + } + }, + "node_modules/gulp-mp-npm/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/gulp-plumber": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/gulp-plumber/-/gulp-plumber-1.2.1.tgz", + "integrity": "sha512-mctAi9msEAG7XzW5ytDVZ9PxWMzzi1pS2rBH7lA095DhMa6KEXjm+St0GOCc567pJKJ/oCvosVAZEpAey0q2eQ==", + "dev": true, + "dependencies": { + "chalk": "^1.1.3", + "fancy-log": "^1.3.2", + "plugin-error": "^0.1.2", + "through2": "^2.0.3" + }, + "engines": { + "node": ">=0.10", + "npm": ">=1.2.10" + } + }, + "node_modules/gulp-plumber/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha512-OQwDZUqYaQwyyhDJHThmzId8daf4/RFNLaeh3AevmSeZ5Y7ug4Ga/yKc6l6kTZOBW781rCj103ZuTh8GAsB3+Q==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha512-t5db90jq+qdgk8aFnxEkjqta0B/GHrM1pxzuuZz2zWsOXc5nKu3t+76s/PQBA8FTcM/ipspIH9jWG4OxCBc2eA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha512-rlVfZW/1Ph2SNySXwR9QYkChp8EkOEiTMO5Vwx60usw04i4nWemkm9RXmQqgkQFaLHsqLuADvjp6IfgL9l2M8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha512-L7AGmkO6jhDkEBBGWlLtftA80Xq8DipnrRPr0pyi7GQLXkaq9JYA4xF4z6qnadIC6euiTDKco0cGSU9muw+WTw==", + "dev": true, + "dependencies": { + "kind-of": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha512-aUH6ElPnMGon2/YkxRIigV32MOpTVcoXQ1Oo8aYn40s+sJ3j+0gFZsT8HKDcxNy7Fi9zuquWtGaGAahOdv5p/g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha512-WzZHcm4+GO34sjFMxQMqZbsz3xiNEgonCskQ9v+IroMmYgk/tas8dG+Hr2D6IbRPybZ12oWpzE/w3cGJ6FJzOw==", + "dev": true, + "dependencies": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-plumber/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/gulp-plumber/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-plumber/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp-plumber/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/gulp-plumber/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-rename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.0.0.tgz", + "integrity": "sha512-97Vba4KBzbYmR5VBs9mWmK+HwIf5mj+/zioxfZhOKeXtx5ZjBk57KFlePf5nxq9QsTtFl0ejnHE3zTC9MHXqyQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gulp-replace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.1.4.tgz", + "integrity": "sha512-SVSF7ikuWKhpAW4l4wapAqPPSToJoiNKsbDoUnRrSgwZHH7lH8pbPeQj1aOVYQrbZKhfSVBxVW+Py7vtulRktw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/vinyl": "^2.0.4", + "istextorbinary": "^3.0.0", + "replacestream": "^4.0.3", + "yargs-parser": ">=5.0.0-security.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gulp-replace-task": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gulp-replace-task/-/gulp-replace-task-2.0.1.tgz", + "integrity": "sha512-Z+fqqbwNkILZ9spAskkl3TcvAAgmwTjOI4HEyIPdOAb7D+rIsV9b0byUuo0y+PWFZWwnI1X1GcCErPkboK3vqQ==", + "dev": true, + "dependencies": { + "applause": "^2.0.0", + "plugin-error": "^1.0.1", + "through2": "^4.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gulp-sourcemaps": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-3.0.0.tgz", + "integrity": "sha512-RqvUckJkuYqy4VaIH60RMal4ZtG0IbQ6PXMNkNsshEGJ9cldUPRb/YCgboYae+CLAs1HQNb4ADTKCx65HInquQ==", + "dev": true, + "dependencies": { + "@gulp-sourcemaps/identity-map": "^2.0.1", + "@gulp-sourcemaps/map-sources": "^1.0.0", + "acorn": "^6.4.1", + "convert-source-map": "^1.0.0", + "css": "^3.0.0", + "debug-fabulous": "^1.0.0", + "detect-newline": "^2.0.0", + "graceful-fs": "^4.0.0", + "source-map": "^0.6.0", + "strip-bom-string": "^1.0.0", + "through2": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gulp-sourcemaps/node_modules/acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/gulp-sourcemaps/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/gulp-sourcemaps/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/gulp-sourcemaps/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/gulp-sourcemaps/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/gulp-typescript": { + "version": "6.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-6.0.0-alpha.1.tgz", + "integrity": "sha512-KoT0TTfjfT7w3JItHkgFH1T/zK4oXWC+a8xxKfniRfVcA0Fa1bKrIhztYelYmb+95RB80OLMBreknYkdwzdi2Q==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1", + "plugin-error": "^1.0.1", + "source-map": "^0.7.3", + "through2": "^3.0.1", + "vinyl": "^2.2.0", + "vinyl-fs": "^3.0.3" + }, + "engines": { + "node": ">= 8" + }, + "peerDependencies": { + "typescript": "~2.7.1 || >=2.8.0-dev || >=2.9.0-dev || ~3.0.0 || >=3.0.0-dev || >=3.1.0-dev || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.7.0-dev " + } + }, + "node_modules/gulp-typescript/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/gulp-typescript/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/gulp/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "node_modules/gulp/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "node_modules/gulp/node_modules/gulp-cli": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", + "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.4.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.2.0", + "yargs": "^7.1.0" + }, + "bin": { + "gulp": "bin/gulp.js" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/gulp/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/gulp/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/gulp/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "dev": true, + "dependencies": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gulp/node_modules/y18n": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", + "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", + "dev": true + }, + "node_modules/gulp/node_modules/yargs": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", + "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.1" + } + }, + "node_modules/gulp/node_modules/yargs-parser": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", + "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "dev": true, + "dependencies": { + "camelcase": "^3.0.0", + "object.assign": "^4.1.0" + } + }, + "node_modules/gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha512-hm6N8nrm3Y08jXie48jsC55eCZz9mnb4OirAStEk2deqeyhXU3C1otDVh+ccttMuc1sBi6RX6ZJ720hs9RCvgw==", + "dev": true, + "dependencies": { + "glogg": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", + "dev": true, + "dependencies": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-values/node_modules/kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hash-sum": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", + "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "dependencies": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/hybrids": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/hybrids/-/hybrids-7.1.0.tgz", + "integrity": "sha512-ZPguGrSfulNM92+e2MyvtFdCDEW4gwi0atHr+GLrD4vc+kLWbYkBdVvYSKXNwMEa1WNfO/6fcegZSd2FHuC/kQ==", + "dev": true + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/import-regex/-/import-regex-1.1.0.tgz", + "integrity": "sha512-EblpleIyIdATUKj8ovFojUHyToxgjeKXQgTHZBGZ4cEkbtV21BlO1PSrzZQ6Fei2fgk7uhDeEx656yvPhlRGeA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "dependencies": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-accessor-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dev": true, + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", + "dev": true, + "dependencies": { + "binary-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", + "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "dev": true, + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", + "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-data-descriptor/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-descriptor/node_modules/kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "optional": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "dev": true + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true + }, + "node_modules/is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "dependencies": { + "is-unc-path": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "dev": true, + "dependencies": { + "text-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "dependencies": { + "unc-path-regex": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true + }, + "node_modules/is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "optional": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istextorbinary": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-3.3.0.tgz", + "integrity": "sha512-Tvq1W6NAcZeJ8op+Hq7tdZ434rqnMx4CCZ7H0ff83uEloDvVbqAwaMTZcafKGJT0VHkYzuXUiCY4hlXQg6WfoQ==", + "dev": true, + "dependencies": { + "binaryextensions": "^2.2.0", + "textextensions": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/j-component": { + "version": "1.4.9", + "resolved": "https://registry.npmjs.org/j-component/-/j-component-1.4.9.tgz", + "integrity": "sha512-7TaTylECTW4sRaDLaj463sTj9BK6/3rSD67um47ypLPwtZW3wPwynCQ9sdnEJmTIw9Jfy2ZLKWiSDRdaINv50w==", + "dev": true, + "dependencies": { + "expr-parser": "^1.0.0", + "miniprogram-api-typings": "^3.2.2", + "miniprogram-exparser": "latest" + } + }, + "node_modules/jest": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-26.6.3.tgz", + "integrity": "sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==", + "dev": true, + "dependencies": { + "@jest/core": "^26.6.3", + "import-local": "^3.0.2", + "jest-cli": "^26.6.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-changed-files": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", + "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "execa": "^4.0.0", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-changed-files/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/jest-changed-files/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-changed-files/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/jest-config": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", + "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^26.6.3", + "@jest/types": "^26.6.2", + "babel-jest": "^26.6.3", + "chalk": "^4.0.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.1", + "graceful-fs": "^4.2.4", + "jest-environment-jsdom": "^26.6.2", + "jest-environment-node": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-jasmine2": "^26.6.3", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + }, + "peerDependencies": { + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-config/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/jest-config/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-diff/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-diff/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-docblock": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", + "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-docblock/node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", + "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-each/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each/node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-each/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/jest-each/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", + "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2", + "jsdom": "^16.4.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-environment-jsdom/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-jsdom/node_modules/jsdom": { + "version": "16.7.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", + "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", + "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", + "dev": true, + "dependencies": { + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "jest-mock": "^26.6.2", + "jest-util": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", + "dev": true, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", + "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/graceful-fs": "^4.1.2", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.4", + "jest-regex-util": "^26.0.0", + "jest-serializer": "^26.6.2", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "micromatch": "^4.0.2", + "sane": "^4.0.3", + "walker": "^1.0.7" + }, + "engines": { + "node": ">= 10.14.2" + }, + "optionalDependencies": { + "fsevents": "^2.1.2" + } + }, + "node_modules/jest-html-reporter": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/jest-html-reporter/-/jest-html-reporter-3.9.0.tgz", + "integrity": "sha512-973Hn/wVWGbZHFyV5T6fcFWpMUl6JUQiDW/IugU6QLdOpnT3fZqdsvVRh/KB07Ov9V8TiRy1oaE2GUfzpVns0g==", + "dev": true, + "dependencies": { + "@babel/core": "^7.9.0", + "@babel/preset-env": "^7.8.7", + "@babel/preset-typescript": "^7.8.3", + "@jest/console": "^29.0.2", + "@jest/test-result": "^29.0.2", + "@jest/types": "^29.0.2", + "dateformat": "3.0.2", + "mkdirp": "^1.0.3", + "sinon": "^9.0.1", + "strip-ansi": "6.0.1", + "xmlbuilder": "15.0.0" + }, + "engines": { + "node": ">=4.8.3" + }, + "peerDependencies": { + "jest": "19.x - 29.x", + "typescript": "^3.7.x || ^4.3.x || ^5.x" + } + }, + "node_modules/jest-html-reporter/node_modules/@jest/console": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", + "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-html-reporter/node_modules/@jest/test-result": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", + "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-html-reporter/node_modules/@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-html-reporter/node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/jest-html-reporter/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-html-reporter/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-html-reporter/node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-html-reporter/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-html-reporter/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-html-reporter/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-html-reporter/node_modules/jest-message-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.5.0", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-html-reporter/node_modules/jest-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-html-reporter/node_modules/pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-html-reporter/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-html-reporter/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-html-reporter/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-jasmine2": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", + "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", + "dev": true, + "dependencies": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "expect": "^26.6.2", + "is-generator-fn": "^2.0.0", + "jest-each": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "pretty-format": "^26.6.2", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-jasmine2/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-jasmine2/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-jasmine2/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-jasmine2/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-jasmine2/node_modules/diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-jasmine2/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-jasmine2/node_modules/jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-jasmine2/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/jest-jasmine2/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-leak-detector": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", + "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", + "dev": true, + "dependencies": { + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-leak-detector/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-leak-detector/node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-matcher-utils/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", + "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "micromatch": "^4.0.2", + "pretty-format": "^26.6.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-message-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/jest-message-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", + "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", + "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", + "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^26.6.2", + "read-pkg-up": "^7.0.1", + "resolve": "^1.18.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", + "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-snapshot": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-resolve/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-resolve/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", + "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.7.1", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-docblock": "^26.0.0", + "jest-haste-map": "^26.6.2", + "jest-leak-detector": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "jest-runtime": "^26.6.3", + "jest-util": "^26.6.2", + "jest-worker": "^26.6.2", + "source-map-support": "^0.5.6", + "throat": "^5.0.0" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-runner/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-runner/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", + "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", + "dev": true, + "dependencies": { + "@jest/console": "^26.6.2", + "@jest/environment": "^26.6.2", + "@jest/fake-timers": "^26.6.2", + "@jest/globals": "^26.6.2", + "@jest/source-map": "^26.6.2", + "@jest/test-result": "^26.6.2", + "@jest/transform": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0", + "cjs-module-lexer": "^0.6.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.4", + "jest-config": "^26.6.3", + "jest-haste-map": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-mock": "^26.6.2", + "jest-regex-util": "^26.0.0", + "jest-resolve": "^26.6.2", + "jest-snapshot": "^26.6.2", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0", + "yargs": "^15.4.1" + }, + "bin": { + "jest-runtime": "bin/jest-runtime.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-runtime/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/jest-runtime/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/jest-runtime/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "node_modules/jest-runtime/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/jest-runtime/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jest-serializer": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", + "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "graceful-fs": "^4.2.4" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", + "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0", + "@jest/types": "^26.6.2", + "@types/babel__traverse": "^7.0.4", + "@types/prettier": "^2.0.0", + "chalk": "^4.0.0", + "expect": "^26.6.2", + "graceful-fs": "^4.2.4", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "jest-haste-map": "^26.6.2", + "jest-matcher-utils": "^26.6.2", + "jest-message-util": "^26.6.2", + "jest-resolve": "^26.6.2", + "natural-compare": "^1.4.0", + "pretty-format": "^26.6.2", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-snapshot/node_modules/diff-sequences": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", + "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/jest-diff": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", + "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot/node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", + "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^26.6.2", + "jest-get-type": "^26.3.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-snapshot/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-snapshot/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/jest-util": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", + "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "@types/node": "*", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", + "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "camelcase": "^6.0.0", + "chalk": "^4.0.0", + "jest-get-type": "^26.3.0", + "leven": "^3.1.0", + "pretty-format": "^26.6.2" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-validate/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-validate/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/node_modules/jest-get-type": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", + "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "dev": true, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "dev": true, + "dependencies": { + "@jest/types": "^26.6.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/jest-validate/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", + "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "jest-util": "^26.6.2", + "string-length": "^4.0.1" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest-watcher/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest-watcher/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/jest/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest/node_modules/jest-cli": { + "version": "26.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", + "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", + "dev": true, + "dependencies": { + "@jest/core": "^26.6.3", + "@jest/test-result": "^26.6.2", + "@jest/types": "^26.6.2", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.4", + "import-local": "^3.0.2", + "is-ci": "^2.0.0", + "jest-config": "^26.6.3", + "jest-util": "^26.6.2", + "jest-validate": "^26.6.2", + "prompts": "^2.0.1", + "yargs": "^15.4.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": ">= 10.14.2" + } + }, + "node_modules/jest/node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest/node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "node_modules/jest/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/jest/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jimp": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.6.8.tgz", + "integrity": "sha512-F7emeG7Hp61IM8VFbNvWENLTuHe0ghizWPuP4JS9ujx2r5mCVYEd/zdaz6M2M42ZdN41blxPajLWl9FXo7Mr2Q==", + "dev": true, + "dependencies": { + "@jimp/custom": "^0.6.8", + "@jimp/plugins": "^0.6.8", + "@jimp/types": "^0.6.8", + "core-js": "^2.5.7", + "regenerator-runtime": "^0.13.3" + } + }, + "node_modules/jpeg-js": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.3.7.tgz", + "integrity": "sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/jsdom/node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/jsdom/node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/jsdom/node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsdom/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jsdom/node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/jsdom/node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/just-debounce": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz", + "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==", + "dev": true + }, + "node_modules/just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", + "dev": true + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/known-css-properties": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.21.0.tgz", + "integrity": "sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==", + "dev": true + }, + "node_modules/last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha512-U/VxvpX4N/rFvPzr3qG5EtLKEnNI0emvIQB3/ecEwv+8GHaUKbIB8vxv1Oai5FAF0d0r7LXHhLLe5K/yChm5GQ==", + "dev": true, + "dependencies": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", + "dev": true, + "dependencies": { + "invert-kv": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha512-IpSVCk9AYvLHo5ctcIXxOBpMWUe+4TKN3VPWAKUbJikkmsGp0VrSM8IttVc32D6J4WUsiPE6aEFRNmIoF/gdow==", + "dev": true, + "dependencies": { + "flush-write-stream": "^1.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "dev": true + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/licia": { + "version": "1.38.1", + "resolved": "https://registry.npmjs.org/licia/-/licia-1.38.1.tgz", + "integrity": "sha512-2CRiNVqkI48Om+eA4cvYn1+bxxHUYlt3XCR1RtuBK/y0Uu7OiWYSgKKHX2KGIaBxuSZjLSqp7zTNBXJgxuSpzg==", + "dev": true + }, + "node_modules/liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "dev": true, + "dependencies": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/liftoff/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/liftoff/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/liftoff/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/lint-staged": { + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.4.tgz", + "integrity": "sha512-EechC3DdFic/TdOPgj/RB3FicqE6932LTHCUm0Y2fsD9KGlLB+RwJl2q1IYBIvEsKzDOgn0D4gll+YxG5RsrKg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.2.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.2.0", + "dedent": "^0.7.0", + "enquirer": "^2.3.6", + "execa": "^4.1.0", + "listr2": "^3.2.2", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/lint-staged/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/lint-staged/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", + "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.1", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/load-bmfont": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.1.tgz", + "integrity": "sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==", + "dev": true, + "dependencies": { + "buffer-equal": "0.0.1", + "mime": "^1.3.4", + "parse-bmfont-ascii": "^1.0.3", + "parse-bmfont-binary": "^1.0.5", + "parse-bmfont-xml": "^1.1.4", + "phin": "^2.9.1", + "xhr": "^2.0.1", + "xtend": "^4.0.0" + } + }, + "node_modules/load-bmfont/node_modules/buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true + }, + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-update/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/longest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", + "integrity": "sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/longest-streak": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", + "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "dev": true, + "dependencies": { + "es5-ext": "~0.10.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", + "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", + "dev": true, + "dependencies": { + "object-visit": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-anchor": { + "version": "8.6.7", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.7.tgz", + "integrity": "sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==", + "dev": true, + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdown-it-attrs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/markdown-it-attrs/-/markdown-it-attrs-4.1.6.tgz", + "integrity": "sha512-O7PDKZlN8RFMyDX13JnctQompwrrILuz2y43pW2GagcwpIIElkAdfeek+erHfxUOlXWPsjFeWmZ8ch1xtRLWpA==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "markdown-it": ">= 9.0.0" + } + }, + "node_modules/markdown-it-container": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/markdown-it-container/-/markdown-it-container-3.0.0.tgz", + "integrity": "sha512-y6oKTq4BB9OQuY/KLfk/O3ysFhB3IMYoIWhGJEidXt1NQFocFK2sA2t0NYZAMyMShAGL6x5OPIbrmXPIqaN9rw==", + "dev": true + }, + "node_modules/markdown-it-emoji": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-2.0.2.tgz", + "integrity": "sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ==", + "dev": true + }, + "node_modules/markdown-it-toc-done-right": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/markdown-it-toc-done-right/-/markdown-it-toc-done-right-4.2.0.tgz", + "integrity": "sha512-UB/IbzjWazwTlNAX0pvWNlJS8NKsOQ4syrXZQ/C72j+jirrsjVRT627lCaylrKJFBQWfRsPmIVQie8x38DEhAQ==", + "dev": true + }, + "node_modules/markdown-it/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha512-LFgVbaHIHMqCRuCZyfCtUOq9/Lnzhi7Z0KFUE2fhD54+JN2jLh3hC02RLkqauJ3U4soU6H1J3tfj/Byk7GoEjA==", + "dev": true, + "dependencies": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/matchdep/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g==", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/matchdep/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/matchdep/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", + "dev": true, + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", + "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "longest-streak": "^2.0.0", + "mdast-util-to-string": "^2.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.0.0", + "zwitch": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdn-data": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-1.1.4.tgz", + "integrity": "sha512-FSYbp3lyKjyj3E7fMl6rYvUdX0FBXaluGqlFoYESWQlyUTq8R+wp0rkFxoYFqZlHCvsUXGjyJmLQSnXToYhOSA==", + "dev": true + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, + "node_modules/memoizee": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", + "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", + "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "2.11.4", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "debug": "^4.0.0", + "parse-entities": "^2.0.0" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "dev": true, + "dependencies": { + "dom-walk": "^0.1.0" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/miniprogram-api-typings": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/miniprogram-api-typings/-/miniprogram-api-typings-3.9.1.tgz", + "integrity": "sha512-oyratzOWyuFhBzONp06l0FBPu03ltCd1sRWoy2v38SnAKxtpZ8ySLTSEw//hIsBdocMda7fFZEjOG57L57mcUw==", + "dev": true + }, + "node_modules/miniprogram-automator": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/miniprogram-automator/-/miniprogram-automator-0.10.0.tgz", + "integrity": "sha512-MsP4k5eQmCwcsII5vmL0DwAXYcqbz6OEYeGIaa6SD9C3Ox/eA6RsHlmtynL/39C7yviZ08nqy51UmwuOEjGeeQ==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "jimp": "^0.6.4", + "licia": "^1.4.4", + "qrcode-reader": "^1.0.4", + "qrcode-terminal": "^0.12.0", + "ws": "^6.1.3" + } + }, + "node_modules/miniprogram-automator/node_modules/ws": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dev": true, + "dependencies": { + "async-limiter": "~1.0.0" + } + }, + "node_modules/miniprogram-compiler": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/miniprogram-compiler/-/miniprogram-compiler-0.2.2.tgz", + "integrity": "sha512-fiJXv/15jCcRAU8YKcO7S7fkPKLa5ZBgpLN+d6B3r3KMktM5tAkDEQ+zm6aTfNoHurYOHcRyPyGf26gqQXlFXg==", + "dev": true, + "dependencies": { + "glob": "^7.1.3", + "unescape-js": "^1.1.1" + } + }, + "node_modules/miniprogram-compiler/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/miniprogram-exparser": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/miniprogram-exparser/-/miniprogram-exparser-2.29.1.tgz", + "integrity": "sha512-f2LUVYcQ5O664nOHhrEbtR//hlqln88dRY0mIwuRncJfuXMCdK9FBk0vzNDG6EgaaeTt3iGLeFQLRHlhYktkXw==", + "dev": true + }, + "node_modules/miniprogram-simulate": { + "version": "1.5.9", + "resolved": "https://registry.npmjs.org/miniprogram-simulate/-/miniprogram-simulate-1.5.9.tgz", + "integrity": "sha512-l/Ddm/L7tZ1I9/V86JnDJpM5fGqw53LieQIkuyIJyyC4/8lBjBQ0ziMCBwb+1EO2aKz4Uhlt4moT4PS/s9QAjQ==", + "dev": true, + "dependencies": { + "csso": "^3.5.1", + "j-component": "^1.4.9", + "less": "^3.10.3", + "miniprogram-compiler": "latest", + "postcss": "^7.0.23" + } + }, + "node_modules/miniprogram-simulate/node_modules/less": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/less/-/less-3.13.1.tgz", + "integrity": "sha512-SwA1aQXGUvp+P5XdZslUOhhLnClSLIjWvJhmd+Vgib5BFIr9lMNlQwmwUNOjXThF/A0x+MCYYPeWEfeWiLRnTw==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "tslib": "^1.10.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "native-request": "^1.0.5", + "source-map": "~0.6.0" + } + }, + "node_modules/miniprogram-simulate/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/miniprogram-simulate/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/miniprogram-simulate/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/miniprogram-simulate/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/miniprogram-simulate/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "dependencies": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-deep/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/module-definition": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/module-definition/-/module-definition-3.4.0.tgz", + "integrity": "sha512-XxJ88R1v458pifaSkPNLUTdSPNVGMP2SXVncVmApGO+gAfrLANiYe6JofymCzVceGOMwQE2xogxBSc8uB7XegA==", + "dev": true, + "dependencies": { + "ast-module-types": "^3.0.0", + "node-source-walk": "^4.0.0" + }, + "bin": { + "module-definition": "bin/cli.js" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "dev": true, + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/native-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.1.0.tgz", + "integrity": "sha512-uZ5rQaeRn15XmpgE0xoPL8YWqcX90VtCFglYwAgkvKM5e8fog+vePLAhHxuuv/gRkrQxIeh5U3q9sMNUrENqWw==", + "dev": true, + "optional": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/needle": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/nise": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-4.1.0.tgz", + "integrity": "sha512-eQMEmGN/8arp0xsvGoQ+B1qvSkR73B1nWSCh7nOt5neMCtwcQVYQGdzQMhcNscktTsWB54xnlSQFzOAPJD8nXA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0", + "@sinonjs/fake-timers": "^6.0.0", + "@sinonjs/text-encoding": "^0.7.1", + "just-extend": "^4.0.2", + "path-to-regexp": "^1.7.0" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-notifier": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", + "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", + "dev": true, + "optional": true, + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "node_modules/node-notifier/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-notifier/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "optional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-notifier/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "optional": true + }, + "node_modules/node-releases": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", + "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "dev": true + }, + "node_modules/node-source-walk": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-4.3.0.tgz", + "integrity": "sha512-8Q1hXew6ETzqKRAs3jjLioSxNfT1cx74ooiF8RlAONwVMcfq+UdzLC2eB5qcPldUxaE5w3ytLkrmV1TGddhZTA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", + "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-package-data/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-selector": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", + "integrity": "sha512-dxvWdI8gw6eAvk9BlPffgEoGfM7AdijoCwOEJge3e3ulT2XLgmU7KvvxprOaCu05Q1uGRHmOhHe1r6emZoKyFw==", + "dev": true + }, + "node_modules/now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "dev": true, + "dependencies": { + "once": "^1.3.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/npm-run-all/node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/npm-run-all/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nprogress": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", + "dev": true + }, + "node_modules/null-check": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz", + "integrity": "sha512-j8ZNHg19TyIQOWCGeeQJBuu6xZYIEurf8M1Qsfd8mFrGEfIZytbw18YjKWg+LcO25NowXGZXZpKAx+Ui3TFfDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==", + "dev": true + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.4.tgz", + "integrity": "sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", + "dev": true, + "dependencies": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-copy/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", + "dev": true, + "dependencies": { + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", + "dev": true, + "dependencies": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.entries": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", + "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz", + "integrity": "sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ==", + "dev": true, + "dependencies": { + "array.prototype.reduce": "^1.0.5", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.21.2", + "safe-array-concat": "^1.0.0" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", + "dev": true, + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha512-naLhxxpUESbNkRqc35oQ2scZSJueHGQNUfMW/0U37IgN6tE2dgDWg3whf+NEliy3F/QysrO48XKUz/nGPe+AQw==", + "dev": true, + "dependencies": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/omggif": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", + "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optional-require": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/optional-require/-/optional-require-1.1.8.tgz", + "integrity": "sha512-jq83qaUb0wNg9Krv1c5OQ+58EK+vHde6aBPzLvPPqJm89UQWsvSuFy9X/OSNJnFeSOKo7btE0n8Nl2+nE+z5nA==", + "dev": true, + "dependencies": { + "require-at": "^1.0.6" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha512-Z87aSjx3r5c0ZB7bcJqIgIRX5bxR7A4aSzvIbaxd0oTkWBCOoKfuGHiKj60CHVUgg1Phm5yMZzBdt8XqRs73Mw==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.1" + } + }, + "node_modules/ordered-read-streams/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/ordered-read-streams/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/ordered-read-streams/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", + "dev": true, + "dependencies": { + "lcid": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-each-series": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", + "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", + "dev": true + }, + "node_modules/parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", + "dev": true + }, + "node_modules/parse-bmfont-xml": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz", + "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==", + "dev": true, + "dependencies": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.4.5" + } + }, + "node_modules/parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dev": true, + "dependencies": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", + "dev": true, + "dependencies": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==", + "dev": true + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", + "dev": true, + "dependencies": { + "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "dependencies": { + "isarray": "0.0.1" + } + }, + "node_modules/path-to-regexp/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/phin": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", + "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pixelmatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", + "integrity": "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==", + "dev": true, + "dependencies": { + "pngjs": "^3.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/playwright": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.33.0.tgz", + "integrity": "sha512-+zzU3V2TslRX2ETBRgQKsKytYBkJeLZ2xzUj4JohnZnxQnivoUvOvNbRBYWSYykQTO0Y4zb8NwZTYFUO+EpPBQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "playwright-core": "1.33.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/playwright/node_modules/playwright-core": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.33.0.tgz", + "integrity": "sha512-aizyPE1Cj62vAECdph1iaMILpT0WUDCq3E6rW6I+dleSbBoGbktvJtzS6VHkZ4DKNEOG9qJpiom/ZxO+S15LAw==", + "dev": true, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "dev": true, + "dependencies": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/plugin-error/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "dependencies": { + "ansi-wrap": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postcss": { + "version": "8.4.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", + "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-html": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.36.0.tgz", + "integrity": "sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw==", + "dev": true, + "dependencies": { + "htmlparser2": "^3.10.0" + }, + "peerDependencies": { + "postcss": ">=5.0.0", + "postcss-syntax": ">=0.36.0" + } + }, + "node_modules/postcss-less": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-3.1.4.tgz", + "integrity": "sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.14" + }, + "engines": { + "node": ">=6.14.4" + } + }, + "node_modules/postcss-less/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-less/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", + "dev": true + }, + "node_modules/postcss-safe-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", + "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==", + "dev": true, + "dependencies": { + "postcss": "^7.0.26" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-safe-parser/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-safe-parser/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-sass": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.4.4.tgz", + "integrity": "sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg==", + "dev": true, + "dependencies": { + "gonzales-pe": "^4.3.0", + "postcss": "^7.0.21" + } + }, + "node_modules/postcss-sass/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-sass/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-scss": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.1.1.tgz", + "integrity": "sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.6" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/postcss-scss/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss-scss/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz", + "integrity": "sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-syntax": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.36.2.tgz", + "integrity": "sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==", + "dev": true, + "peerDependencies": { + "postcss": ">=5.0.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "dev": true, + "dependencies": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + }, + "engines": { + "node": ">=6.14.4" + } + }, + "node_modules/precinct": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/precinct/-/precinct-8.3.1.tgz", + "integrity": "sha512-pVppfMWLp2wF68rwHqBIpPBYY8Kd12lDhk8LVQzOwqllifVR15qNFyod43YLyFpurKRZQKnE7E4pofAagDOm2Q==", + "dev": true, + "dependencies": { + "commander": "^2.20.3", + "debug": "^4.3.3", + "detective-amd": "^3.1.0", + "detective-cjs": "^3.1.1", + "detective-es6": "^2.2.1", + "detective-less": "^1.0.2", + "detective-postcss": "^4.0.0", + "detective-sass": "^3.0.1", + "detective-scss": "^2.0.1", + "detective-stylus": "^1.0.0", + "detective-typescript": "^7.0.0", + "module-definition": "^3.3.1", + "node-source-walk": "^4.2.0" + }, + "bin": { + "precinct": "bin/cli.js" + }, + "engines": { + "node": "^10.13 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/prismjs": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "dependencies": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "node_modules/pumpify/node_modules/pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "dev": true, + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qrcode": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz", + "integrity": "sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==", + "dev": true, + "dependencies": { + "dijkstrajs": "^1.0.1", + "encode-utf8": "^1.0.3", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode-reader": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/qrcode-reader/-/qrcode-reader-1.0.4.tgz", + "integrity": "sha512-rRjALGNh9zVqvweg1j5OKIQKNsw3bLC+7qwlnead5K/9cb1cEIAGkwikt/09U0K+2IDWGD9CC6SP7tHAjUeqvQ==", + "dev": true + }, + "node_modules/qrcode-terminal": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", + "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", + "dev": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/qrcode/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/qrcode/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/qrcode/node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/qrcode/node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/qrcode/node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/queue-tick": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", + "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", + "dev": true + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, + "node_modules/read-package-json": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz", + "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==", + "dev": true, + "dependencies": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^2.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "node_modules/read-package-json/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-package-json/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-package-json/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-package-tree": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz", + "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", + "deprecated": "The functionality that this package provided is now in @npmcli/arborist", + "dev": true, + "dependencies": { + "read-package-json": "^2.0.0", + "readdir-scoped-modules": "^1.0.0", + "util-promisify": "^2.1.0" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "node_modules/readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/readdirp/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readdirp/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/readdirp/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/readdirp/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", + "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dev": true, + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/remark": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/remark/-/remark-13.0.0.tgz", + "integrity": "sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA==", + "dev": true, + "dependencies": { + "remark-parse": "^9.0.0", + "remark-stringify": "^9.0.0", + "unified": "^9.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", + "dev": true, + "dependencies": { + "mdast-util-from-markdown": "^0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-9.0.1.tgz", + "integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==", + "dev": true, + "dependencies": { + "mdast-util-to-markdown": "^0.6.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha512-wigO8/O08XHb8YPzpDDT+QmRANfW6vLqxfaXm1YXhnFf3AkSLyjfG3GEFg4McZkmgL7KvCj5u2KczkvSP6NfHA==", + "dev": true, + "dependencies": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/remove-bom-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/remove-bom-stream/node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/remove-bom-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/remove-bom-stream/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/remove-bom-stream/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "node_modules/repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha512-CHPV/GAglbIB1tnQgaiysb8H2yCy8WQ7lcEwQ/eT+kLj0QHV8LnJW0zpqpE7RSkrMSRoa+EBoag86clf7WAgSg==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/replacestream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", + "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.3", + "object-assign": "^4.0.1", + "readable-stream": "^2.0.2" + } + }, + "node_modules/replacestream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/replacestream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/replacestream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/require-at": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/require-at/-/require-at-1.0.6.tgz", + "integrity": "sha512-7i1auJbMUrXEAZCOQ0VNJgmcT2VOKPRl2YGJwgpHpC9CE91Mv4/4UYIUm4chGJaI381ZDq1JUicFii64Hapd8g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", + "dev": true + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", + "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", + "dev": true, + "dependencies": { + "global-dirs": "^0.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha512-NYDgziiroVeDC29xq7bp/CacZERYsA9bXYd1ZmcJlF3BcrZv5pTb4NG7SjdyKDnXZ84aC4vo2u6sNKIA1LCu/A==", + "dev": true, + "dependencies": { + "value-or-function": "^3.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "2.77.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.3.tgz", + "integrity": "sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=10.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true, + "engines": { + "node": "6.* || >= 7.*" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "dev": true + }, + "node_modules/safe-array-concat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", + "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "dev": true, + "dependencies": { + "ret": "~0.1.10" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "deprecated": "some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added", + "dev": true, + "dependencies": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + }, + "bin": { + "sane": "src/cli.js" + }, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/sane/node_modules/anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "dependencies": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "node_modules/sane/node_modules/braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/braces/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/sane/node_modules/execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sane/node_modules/fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/fill-range/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/sane/node_modules/is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-number/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "dependencies": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "dev": true, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sane/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/sane/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/sane/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "dependencies": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sane/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/saxes": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", + "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/section-matter/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true + }, + "node_modules/semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha512-Ny/iyOzSSa8M5ML46IAx3iXc6tfOsYU2R4AXi2UpHk60Zrgyq6eqPj/xiOfS0rRl/iiQ/rdJkVjw/5cdUyCntQ==", + "dev": true, + "dependencies": { + "sver-compat": "^1.5.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "dependencies": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/set-value/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true, + "optional": true + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sinon": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.8.1", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/samsam": "^5.3.1", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "dependencies": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "dependencies": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", + "dev": true, + "dependencies": { + "is-descriptor": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-node/node_modules/is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "dependencies": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "dependencies": { + "kind-of": "^3.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon-util/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/snapdragon/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/snapdragon/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/snapdragon/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "deprecated": "See https://github.com/lydell/source-map-url#deprecated", + "dev": true + }, + "node_modules/sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "dev": true + }, + "node_modules/specificity": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "dev": true, + "bin": { + "specificity": "bin/specificity" + } + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "dependencies": { + "extend-shallow": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/standard-changelog": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/standard-changelog/-/standard-changelog-2.0.27.tgz", + "integrity": "sha512-ltjqZfimLVBmAHSJ+U/zBVoVYisz6ankaRgq2UJIJk1tH4wLkYCXw8R02I27q3/UsvGVJu3m66W7raq5sQh1zQ==", + "dev": true, + "dependencies": { + "add-stream": "^1.0.0", + "chalk": "^4.0.0", + "conventional-changelog-angular": "^5.0.12", + "conventional-changelog-core": "^4.2.1", + "figures": "^3.0.0", + "fs-access": "^1.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "rimraf": "^3.0.0", + "sprintf-js": "^1.1.1", + "tempfile": "^3.0.0" + }, + "bin": { + "standard-changelog": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/standard-changelog/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/standard-changelog/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/standard-changelog/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/standard-changelog/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/standard-changelog/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/standard-changelog/node_modules/sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + }, + "node_modules/standard-changelog/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", + "dev": true, + "dependencies": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/static-extend/node_modules/define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", + "dev": true, + "dependencies": { + "is-descriptor": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true + }, + "node_modules/stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "node_modules/streamx": { + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.13.2.tgz", + "integrity": "sha512-+TWqixPhGDXEG9L/XczSbhfkmwAtGs3BJX5QNU6cvno+pOLKeszByWcnaTu6dg8efsTYqR8ZZuXWHhZfgrxMvA==", + "dev": true, + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.fromcodepoint": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string.fromcodepoint/-/string.fromcodepoint-0.2.1.tgz", + "integrity": "sha512-n69H31OnxSGSZyZbgBlvYIXlrMhJQ0dQAX1js1QDhpaUH6zmU3QYlj07bCwCNlPOu3oRXIubGPl2gDGnHsiCqg==", + "dev": true + }, + "node_modules/string.prototype.padend": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz", + "integrity": "sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", + "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/stringify-object/node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", + "dev": true + }, + "node_modules/stylelint": { + "version": "13.13.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.13.1.tgz", + "integrity": "sha512-Mv+BQr5XTUrKqAXmpqm6Ddli6Ief+AiPZkRsIrAoUKFuq/ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ==", + "dev": true, + "dependencies": { + "@stylelint/postcss-css-in-js": "^0.37.2", + "@stylelint/postcss-markdown": "^0.36.2", + "autoprefixer": "^9.8.6", + "balanced-match": "^2.0.0", + "chalk": "^4.1.1", + "cosmiconfig": "^7.0.0", + "debug": "^4.3.1", + "execall": "^2.0.0", + "fast-glob": "^3.2.5", + "fastest-levenshtein": "^1.0.12", + "file-entry-cache": "^6.0.1", + "get-stdin": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^11.0.3", + "globjoin": "^0.1.4", + "html-tags": "^3.1.0", + "ignore": "^5.1.8", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "known-css-properties": "^0.21.0", + "lodash": "^4.17.21", + "log-symbols": "^4.1.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.4", + "normalize-selector": "^0.2.0", + "postcss": "^7.0.35", + "postcss-html": "^0.36.0", + "postcss-less": "^3.1.4", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^4.0.2", + "postcss-sass": "^0.4.4", + "postcss-scss": "^2.1.1", + "postcss-selector-parser": "^6.0.5", + "postcss-syntax": "^0.36.2", + "postcss-value-parser": "^4.1.0", + "resolve-from": "^5.0.0", + "slash": "^3.0.0", + "specificity": "^0.4.1", + "string-width": "^4.2.2", + "strip-ansi": "^6.0.0", + "style-search": "^0.1.0", + "sugarss": "^2.0.0", + "svg-tags": "^1.0.0", + "table": "^6.6.0", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^3.0.3" + }, + "bin": { + "stylelint": "bin/stylelint.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + } + }, + "node_modules/stylelint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "node_modules/stylelint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/stylelint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/stylelint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/stylelint/node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint/node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stylelint/node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stylelint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stylelint/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/stylelint/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/stylelint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/sugarss": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", + "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.2" + } + }, + "node_modules/sugarss/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/sugarss/node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha512-aFTHfmjwizMNlNE6dsGmoAM4lHjL0CyiobWaFiXWSlD7cIxshW422Nb8KbXCmR6z+0ZEPY+daXJrDyh/vuwTyg==", + "dev": true, + "dependencies": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/tdesign-flutter": { + "resolved": "", + "link": true + }, + "node_modules/tdesign-icons-svg": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tdesign-icons-svg/-/tdesign-icons-svg-0.0.1.tgz", + "integrity": "sha512-4l05CgPXwI33Y6hU8ytKYIIXVznR22x6blhhtB6MntSHj2xrA+Unx4U/qy+Do/lCMtYao9q1wVkaZvxNjuX7jw==", + "dev": true + }, + "node_modules/tdesign-icons-view": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/tdesign-icons-view/-/tdesign-icons-view-0.1.9.tgz", + "integrity": "sha512-L+GzMCOetFuYpQYCaJXObB85VxN+zoARKTxdQZurFRq3WB72ygkBb0+iR1A4tVhhzBQsMMPqINkYbQjdVK0Skg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.16.7", + "@github/clipboard-copy-element": "^1.1.2", + "hybrids": "^7.0.3" + } + }, + "node_modules/tdesign-publish-cli": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/tdesign-publish-cli/-/tdesign-publish-cli-0.0.12.tgz", + "integrity": "sha512-eRmrOCUqR/55AAZ8dqUGV8+TU1qzljGoiU9TsFOroHcNe4T1u/dYwEBfg39NsupWUoO/v67XeUDlKyTjUHkO2w==", + "dev": true, + "dependencies": { + "commander": "^7.1.0", + "inquirer": "^8.0.0", + "standard-changelog": "^2.0.27" + }, + "bin": { + "publish-cli": "bin/index.js" + } + }, + "node_modules/tdesign-publish-cli/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tdesign-site-components": { + "version": "0.13.18", + "resolved": "https://registry.npmjs.org/tdesign-site-components/-/tdesign-site-components-0.13.18.tgz", + "integrity": "sha512-A17zVmkRFYaqfnIdKeCq7H9MmgxIs6rr+1rUtX0KAECEyRgvvIYnaj+Mp7aa/FwAhVvgXUNt9Wt7MlSYrOsdwA==", + "dev": true, + "dependencies": { + "@popperjs/core": "^2.11.5", + "hybrids": "8.2.2", + "nprogress": "^0.2.0", + "prismjs": "^1.29.0", + "qrcode": "^1.5.1", + "tdesign-icons-svg": "0.0.1" + } + }, + "node_modules/tdesign-site-components/node_modules/hybrids": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/hybrids/-/hybrids-8.2.2.tgz", + "integrity": "sha512-xCangE3oItxeWMNKHLa0yn0MHi0MHBtGLIz0Ty7PwdcBBN2sQ0mrBvVSf4lLMyBFNO7x47/2lO13FSJB/SkPyw==", + "dev": true, + "bin": { + "hybrids": "cli/index.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tempfile": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-3.0.0.tgz", + "integrity": "sha512-uNFCg478XovRi85iD42egu+eSFUmmka750Jy7L5tfHI5hQKKtbPnxaSaXAbBqCDYrw3wx4tXjKwci4/QmsZJxw==", + "dev": true, + "dependencies": { + "temp-dir": "^2.0.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tempfile/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ternary-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz", + "integrity": "sha512-oIzdi+UL/JdktkT+7KU5tSIQjj8pbShj3OASuvDEhm0NT5lppsm7aXWAmAq4/QMaBIyfuEcNLbAQA+HpaISobQ==", + "dev": true, + "dependencies": { + "duplexify": "^4.1.1", + "fork-stream": "^0.0.4", + "merge-stream": "^2.0.0", + "through2": "^3.0.1" + } + }, + "node_modules/ternary-stream/node_modules/duplexify": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", + "integrity": "sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.4.1", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1", + "stream-shift": "^1.0.0" + } + }, + "node_modules/ternary-stream/node_modules/through2": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz", + "integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "2 || 3" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/textextensions": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-3.3.0.tgz", + "integrity": "sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "dependencies": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "node_modules/through2-filter/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2-filter/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/through2-filter/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/through2-filter/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "dev": true, + "dependencies": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, + "node_modules/timm": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/timm/-/timm-1.7.1.tgz", + "integrity": "sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==", + "dev": true + }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", + "dev": true, + "dependencies": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", + "dev": true, + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-object-path/node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "dependencies": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha512-+QIz37Ly7acM4EMdw2PRN389OneM5+d844tirkGp4dPKzI5OE72V9OsbFp+CIYJDahZ41ZV05hNtcPAQUAm9/Q==", + "dev": true, + "dependencies": { + "through2": "^2.0.3" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/to-through/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/to-through/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/to-through/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/to-through/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", + "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tree-to-list": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tree-to-list/-/tree-to-list-2.0.0.tgz", + "integrity": "sha512-KqiNWyEgAhWTdPXj9jElPOiJ9A+g/QusQJO7A1pD8NZkib//Mo3aGfqWn+FbESLcBRw5N0dUV4qvSv2kZeD16g==", + "dev": true + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tsconfig-paths/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/undertaker": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", + "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", + "dev": true, + "dependencies": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "fast-levenshtein": "^1.0.0", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/undertaker/node_modules/fast-levenshtein": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", + "integrity": "sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==", + "dev": true + }, + "node_modules/unescape-js": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unescape-js/-/unescape-js-1.1.4.tgz", + "integrity": "sha512-42SD8NOQEhdYntEiUQdYq/1V/YHwr1HLwlHuTJB5InVVdOSbgI6xu8jK5q65yIzuFCfczzyDF/7hbGzVbyCw0g==", + "dev": true, + "dependencies": { + "string.fromcodepoint": "^0.2.1" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unified": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "dev": true, + "dependencies": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "dependencies": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "dev": true + }, + "node_modules/unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "dev": true, + "dependencies": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "node_modules/unist-util-find-all-after": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz", + "integrity": "sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ==", + "dev": true, + "dependencies": { + "unist-util-is": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unorm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", + "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", + "dev": true, + "dependencies": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", + "dev": true, + "dependencies": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", + "dev": true, + "dependencies": { + "isarray": "1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unset-value/node_modules/has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/uslug": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/uslug/-/uslug-1.0.4.tgz", + "integrity": "sha512-Jrbpp/NS3TvIGNjfJT1sn3/BCeykoxR8GbNYW5lF6fUscLkbXFwj1b7m4DvIkHm8k3Qr6Co68lbTmoZTMGk/ow==", + "dev": true, + "dependencies": { + "unorm": ">= 1.0.0" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/utif": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz", + "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==", + "dev": true, + "dependencies": { + "pako": "^1.0.5" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/util-promisify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz", + "integrity": "sha512-K+5eQPYs14b3+E+hmE2J6gCZ4JmMl9DbYS6BeP2CHq6WMuNxErxf5B/n0fz85L8zUuoO6rIzNNmIQDu/j+1OcA==", + "dev": true, + "dependencies": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz", + "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0", + "source-map": "^0.7.3" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha512-jdBB2FrWvQC/pnPtIqcLsMaQgjhdb6B7tk1MMyTKapox+tQZbdRP4uLxu/JY0t7fbfDCUMnuelzEYv5GsxHhdg==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "dev": true, + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "dependencies": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-fs/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/vinyl-fs/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/vinyl-fs/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/vinyl-fs/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha512-NiibMgt6VJGJmyw7vtzhctDcfKch4e4n9TBeoWlirb7FMg9/1Ov9k+A5ZRAtywBpRPiyECvQRQllYM8dECegVA==", + "dev": true, + "dependencies": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-sourcemap/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "dependencies": { + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", + "dev": true, + "dependencies": { + "source-map": "^0.5.1" + } + }, + "node_modules/vinyl-sourcemaps-apply/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/vinyl/node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/vite": { + "version": "2.9.15", + "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.15.tgz", + "integrity": "sha512-fzMt2jK4vQ3yK56te3Kqpkaeq9DkcZfBbzHwYpobasvgYmP2SoAr6Aic05CsB4CzCZbsDv4sujX3pkEGhLabVQ==", + "dev": true, + "dependencies": { + "esbuild": "^0.14.27", + "postcss": "^8.4.13", + "resolve": "^1.22.0", + "rollup": ">=2.59.0 <2.78.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": ">=12.2.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "less": "*", + "sass": "*", + "stylus": "*" + }, + "peerDependenciesMeta": { + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + } + } + }, + "node_modules/vite-plugin-tdoc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vite-plugin-tdoc/-/vite-plugin-tdoc-2.0.4.tgz", + "integrity": "sha512-KKoEvlhf78opGCb+WjbOc6TRkzqOEm6rLMmJL9POh8OEa6jOCETs1k81TAaPLaIxuoJxqE4k6IAMchV60XoKpw==", + "dev": true, + "dependencies": { + "diacritics": "^1.3.0", + "escape-html": "^1.0.3", + "markdown-it": "^12.3.0", + "markdown-it-anchor": "^8.4.1", + "markdown-it-attrs": "^4.1.0", + "markdown-it-container": "^3.0.0", + "markdown-it-emoji": "^2.0.0", + "markdown-it-toc-done-right": "^4.2.0", + "prismjs": "^1.25.0", + "uslug": "^1.0.4" + } + }, + "node_modules/vue": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.3.2.tgz", + "integrity": "sha512-98hJcAhyDwZoOo2flAQBSPVYG/o0HA9ivIy2ktHshjE+6/q8IMQ+kvDKQzOZTFPxvnNMcGM+zS2A00xeZMA7tA==", + "dev": true, + "dependencies": { + "@vue/compiler-dom": "3.3.2", + "@vue/compiler-sfc": "3.3.2", + "@vue/runtime-dom": "3.3.2", + "@vue/server-renderer": "3.3.2", + "@vue/shared": "3.3.2" + } + }, + "node_modules/vue-router": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.0.tgz", + "integrity": "sha512-c+usESa6ZoWsm4PPdzRSyenp5A4dsUtnDJnrI03fY1IpIihA9TK3x5ffgkFDpjhLJZewsXoKURapNLFdZjuqTg==", + "dev": true, + "dependencies": { + "@vue/devtools-api": "^6.5.0" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", + "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "deprecated": "Use your platform's native performance.now() and performance.timeOrigin.", + "dev": true, + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", + "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", + "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", + "dev": true, + "engines": { + "node": ">=10.4" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", + "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", + "dev": true, + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==", + "dev": true + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "dev": true, + "dependencies": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "node_modules/xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", + "dev": true + }, + "node_modules/xml-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/xml-parser/-/xml-parser-1.2.1.tgz", + "integrity": "sha512-lPUzzmS0zdwcNtyNndCl2IwH172ozkUDqmfmH3FcuDzHVl552Kr6oNfsvteHabqTWhsrMgpijqZ/yT7Wo1/Pzw==", + "dev": true, + "dependencies": { + "debug": "^2.2.0" + } + }, + "node_modules/xml-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/xml-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlbuilder": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.0.0.tgz", + "integrity": "sha512-KLu/G0DoWhkncQ9eHSI6s0/w+T4TM7rQaLhtCaL6tORv8jFlJPlnGumsgTcGfYeS1qZ/IHqrvDG7zJZ4d7e+nw==", + "dev": true, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwitch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/tdesign-site/package.json b/tdesign-site/package.json new file mode 100644 index 000000000..82da512da --- /dev/null +++ b/tdesign-site/package.json @@ -0,0 +1,127 @@ +{ + "name": "tdesign-flutter", + "purename": "tdesign", + "version": "0.1.2", + "description": "tdesign-flutter", + "title": "tdesign-flutter", + "main": "miniprogram_dist/index.js", + "miniprogram": "miniprogram_dist", + "keywords": [ + "tdesign", + "flutter" + ], + "repository": { + "type": "git", + "url": "https://github.com/Tencent/tdesign-flutter" + }, + "homepage": "https://tdesign.tencent.com/flutter", + "scripts": { + "build": "cross-env NODE_ENV=production gulp build --gulpfile script/gulpfile.js --cwd ./", + "build:dist": "gulp --gulpfile script/gulpfile.dist.js --cwd ./", + "build:example": "gulp --gulpfile script/gulpfile.example.js --cwd ./", + "build:assets": "cross-env NODE_ENV=production gulp assets:build --gulpfile script/gulpfile.dist.js --cwd ./", + "update:icons": "node script/update-icons.js", + "lintfix": "eslint '{src,example}/**/*.{js,ts}' --fix", + "lint": "eslint '{src,example}/**/*.{js,ts}'", + "format": "prettier {src,example,script}/**/*.{js,ts,wxss,less,wxml,html,json,md,wxs} --write", + "site": "cd site && vite build", + "site:dev": "cd site && vite", + "site:intranet": "cd site && vite build --mode intranet", + "site:prerender": "node script/prerender.mjs", + "cover": "jest --coverage", + "test": "jest && jest -c jest.virtualHost.config.js && jest -c jest.e2e.config.js", + "test:virtualHost": "jest -c jest.virtualHost.config.js", + "test:snap-update": "npm run test:virtualHost -- -u && npm run test:unit -- -u", + "test:demo": "node gen-demo-test.js", + "test:unit": "jest", + "test:e2e": "jest -c jest.e2e.config.js", + "badge": "node script/coverage-badge.js", + "prepare": "cd .. && husky install tdesign-site/superjsonweb/.husky", + "generate": "gulp generate --gulpfile script/gulpfile.js --cwd ./", + "changelog": "node script/generate-changelog.js", + "robot": "publish-cli robot-msg", + "qrcode": "node script/qrcode/index.js" + }, + "author": "tdesign", + "license": "MIT", + "devDependencies": { + "@babel/core": "^7.12.10", + "@babel/plugin-proposal-decorators": "^7.18.9", + "@babel/preset-env": "^7.12.11", + "@babel/preset-typescript": "^7.12.7", + "@commitlint/cli": "^16.0.2", + "@commitlint/config-conventional": "^16.0.0", + "@rollup/plugin-node-resolve": "^13.0.5", + "@types/jest": "^27.0.3", + "@typescript-eslint/eslint-plugin": "^5.6.0", + "@typescript-eslint/parser": "~5.35.0", + "@vitejs/plugin-vue": "^2.3.3", + "@vitejs/plugin-vue-jsx": "^1.1.7", + "@vue/compiler-sfc": "^3.2.4", + "axios": "^1.1.3", + "babel-jest": "^26.6.3", + "commitizen": "^4.2.4", + "cross-env": "^7.0.2", + "cz-conventional-changelog": "^3.3.0", + "del": "^6.1.1", + "eslint": "^7.0.0", + "eslint-config-airbnb-base": "^14.2.1", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-import": "^2.24.2", + "eslint-plugin-prettier": "^4.0.0", + "glob": "^8.1.0", + "gray-matter": "^4.0.3", + "gulp": "^4.0.2", + "gulp-changed": "^4.0.2", + "gulp-if": "^3.0.0", + "gulp-less": "^5.0.0", + "gulp-mp-npm": "^1.9.7", + "gulp-plumber": "^1.2.1", + "gulp-rename": "^2.0.0", + "gulp-replace": "^1.0.0", + "gulp-replace-task": "^2.0.1", + "gulp-sourcemaps": "^3.0.0", + "gulp-typescript": "^6.0.0-alpha.1", + "husky": "^7.0.4", + "jest": "^26.6.3", + "jest-html-reporter": "^3.3.0", + "jsdom": "^20.0.0", + "less": "^4.1.1", + "lint-staged": "^10.0.0-1", + "lodash": "^4.17.21", + "miniprogram-api-typings": "^3.4.6", + "miniprogram-automator": "^0.10.0", + "miniprogram-simulate": "^1.5.7", + "npm-run-all": "^4.1.5", + "playwright": "^1.19.1", + "prettier": "^2.0.5", + "prismjs": "^1.24.1", + "standard-changelog": "^2.0.27", + "stylelint": "^13.13.1", + "tdesign-icons-view": "^0.1.0", + "tdesign-publish-cli": "^0.0.12", + "tdesign-site-components": "^0.13.18", + "typescript": "~4.7.2", + "vite": "^2.7.6", + "vite-plugin-tdoc": "^2.0.1", + "vue": "^3.2.4", + "vue-router": "^4.0.11" + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + } + }, + "lint-staged": { + "{src,example,script}/**/*.{js,ts,wxml,html,json,less}": [ + "prettier --write" + ], + "{src,example}/**/*.{js,ts}": [ + "eslint --fix" + ] + }, + "dependencies": { + "dayjs": "^1.10.7", + "tdesign-flutter": "file:" + } +} diff --git a/tdesign-site/script/config.js b/tdesign-site/script/config.js new file mode 100644 index 000000000..be83a55c2 --- /dev/null +++ b/tdesign-site/script/config.js @@ -0,0 +1,5 @@ +const config = { + CONFIG_PREFIX: 't', +}; + +module.exports = config; diff --git a/tdesign-site/script/coverage-badge.js b/tdesign-site/script/coverage-badge.js new file mode 100644 index 000000000..038026afc --- /dev/null +++ b/tdesign-site/script/coverage-badge.js @@ -0,0 +1,78 @@ +const path = require('path'); +const fs = require('fs'); +const os = require('os'); + +const specify = process.argv[2]; + +const data = require('../test/unit/coverage/coverage-summary.json'); + +const ans = new Map(); + +Object.keys(data).forEach((fPath) => { + const _fPath = os.platform() === 'win32' ? fPath.slice(2).replace(/\\/g, '/') : fPath; + if (_fPath.startsWith('/')) { + const [, component] = /src\/([\w-]+)\//.exec(_fPath) ?? []; + + if (component) { + if (!fPath.includes('/_example/')) { + const set = data[fPath]; + const target = ans.get(component) ?? { + lines: { total: 0, covered: 0, skipped: 0 }, + functions: { total: 0, covered: 0, skipped: 0 }, + statements: { total: 0, covered: 0, skipped: 0 }, + branches: { total: 0, covered: 0, skipped: 0 }, + }; + + Object.entries(set).forEach(([type, dataset]) => { + Object.entries(dataset).forEach(([category, val]) => { + target[type][category] = val + target[type][category]; + }); + }); + ans.set(component, target); + } + } + } +}); + +ans.forEach((items, component) => { + let svgs = ''; + Object.entries(items).forEach(([type, item]) => { + let val = item.total === 0 ? 100 : ((item.covered / item.total) * 100).toFixed(0); + const relatedMap = { + avatar: 'avatar-group', + button: 'button-group', + cell: 'cell-group', + checkbox: 'checkbox-group', + 'dropdown-menu': 'dropdown-item', + grid: 'grid-item', + picker: 'picker-item', + radio: 'radio-group', + steps: 'step-item', + // swiper: ['swiper-item', 'swiper-nav'], + 'tab-bar': 'tab-bar-item', + tabs: 'tab-panel', + tag: 'check-tag', + }; + + if (component in relatedMap) { + const related = ans.get(relatedMap[component]); + if (related) { + const denominator = item.total + related[type].total; + val = denominator === 0 ? 100 : (((item.covered + related[type].covered) / denominator) * 100).toFixed(0); + } + } + const message = isNaN(val) ? '0' : val; + const color = parseInt(val, 10) >= 80 ? 'blue' : 'red'; + + svgs += ``; + }); + + if ((specify && component === specify) || !specify) { + const fPath = path.resolve(__dirname, `../src/${component}/README.md`); + let readme = fs.readFileSync(fPath, { encoding: 'utf-8' }); + + readme = readme.replace(/\n/g, ''); + readme = readme.replace('## 引入', `${svgs}\n## 引入`); + fs.writeFileSync(fPath, readme); + } +}); diff --git a/tdesign-site/script/generate-changelog.js b/tdesign-site/script/generate-changelog.js new file mode 100644 index 000000000..a802b677a --- /dev/null +++ b/tdesign-site/script/generate-changelog.js @@ -0,0 +1,86 @@ +/* eslint-disable */ +const { execSync } = require('child_process'); +const fs = require('fs'); +const readline = require('readline'); +const standardChangelog = require('standard-changelog'); +const pkg = require('../package.json'); + +const VERSION_REG = /\d+\.\d+\.\d+/; + +function updateVersion() { + return new Promise((resolve) => { + const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); + + rl.setPrompt(`当前 package.json 版本号为: ${pkg.version}\n请输入本次要发布的版本号:(可按回车跳过)\n`); + rl.prompt(); + + // eslint-disable-next-line consistent-return + rl.on('line', (input) => { + let newVersion = ''; + if (!input) { + newVersion = pkg.version.replace(/(\d+\.\d+\.)(\d+)/, (version, $1, $2) => $1 + (Number($2) + 1)); + } else if (!VERSION_REG.test(input)) { + console.log('\x1B[31m%s\x1B[0m', '\n⚡ 不要搞事年轻人,请输入正确版本号格式!\n'); + rl.prompt(); + return; + } else { + newVersion = input; + } + const newPkg = JSON.stringify(Object.assign({}, pkg, { version: newVersion }), null, 2); + fs.writeFileSync('package.json', `${newPkg}\n`, 'utf8'); + console.log('\x1B[32m%s\x1B[0m', '\n🎉 good job! package.json 文件已更新.\n'); + rl.close(); + }); + + rl.on('close', resolve); + }); +} + +function getLastChangeLogCommit() { + const gitCommand = 'git blame CHANGELOG.md'; + const changeLogCommits = execSync(gitCommand, { + cwd: process.cwd(), + encoding: 'utf-8', + }).split('\n'); + + return changeLogCommits.find((cmt) => VERSION_REG.test(cmt)).slice(0, 8); +} + +function getGitCommitMap(lastCommit) { + const gitCommand = `git log --pretty=format:"%H:%cn" ${lastCommit}..HEAD`; + const gitLogMap = execSync(gitCommand, { cwd: process.cwd(), encoding: 'utf-8' }).toString(); + fs.writeFileSync('.gitlogmap', gitLogMap, 'utf8'); +} + +async function updateChangeLog() { + await updateVersion(); + + console.log('\x1B[32m%s\x1B[0m', '正在生成 changeLog... \n'); + + const lastCommit = getLastChangeLogCommit(); + let initialChangelogStr = fs.readFileSync('CHANGELOG.md', 'utf8'); + + const pageDataStr = initialChangelogStr.match(/---[\s\S]+---/)[0] + '\n'; + const data = initialChangelogStr.split(/---[\s\S]+---/); + data.unshift(pageDataStr); + + new Promise((resolve) => { + standardChangelog({}, null, { from: lastCommit, to: 'HEAD' }) + .on('data', (chunk) => { + let changeLogStr = chunk.toString().trim(); + changeLogStr = changeLogStr.replace(/\(([\d\-]+)\)/g, '`$1`'); + changeLogStr = changeLogStr.replace(/^#\s/g, '## ').trim(); + data.splice(1, 0, `${changeLogStr}\n`); + }) + .on('end', resolve); + }).then(() => { + getGitCommitMap(lastCommit); + const writeStream = fs.createWriteStream('CHANGELOG.md', 'utf8'); + writeStream.write(data.join('\n')); + writeStream.end(); + + console.log('\x1B[32m%s\x1B[0m', '已生成最新 changeLog... 请打开 CHANGELOG.md 确认'); + }); +} + +updateChangeLog(); diff --git a/tdesign-site/script/prerender.mjs b/tdesign-site/script/prerender.mjs new file mode 100644 index 000000000..b130507de --- /dev/null +++ b/tdesign-site/script/prerender.mjs @@ -0,0 +1,63 @@ +import fs from 'fs'; +import path from 'path'; +import { chromium } from 'playwright'; +import { preview } from 'vite'; +import siteConfig from '../site/site.config.mjs'; + +const prefix = 'http://127.0.0.1:9999'; +const spiderPath = path.resolve('./_static_site'); + +function initPageList() { + const pageList = []; + + siteConfig.docs.forEach((doc) => { + doc.children.forEach((child) => { + pageList.push(`${prefix}${child.path}`); + }); + }); + + console.log(pageList) + + return pageList; +} + +async function initPreviewServer() { + const previewServer = await preview({ + preview: { port: 9999, open: false }, + build: { outDir: './_site' } + }); + + previewServer.printUrls(); +} + +(async () => { + const pageList = initPageList(); + await initPreviewServer(); + + const browser = await chromium.launch(); + const page = await browser.newPage(); + + try { + fs.mkdirSync(spiderPath); + } catch {} + + for (let url of pageList) { + const [, pathName] = url.split(prefix); + const filePath = `${spiderPath}${pathName || '/index'}.html`; + + console.log('\x1b[35m', `opening ${url}...`); + await page.goto(url); + + console.log('\x1b[34m', `rendering ${url}...`); + const html = await page.content(); + try { + fs.mkdirSync(path.dirname(filePath)); + } catch {} + + console.log('\x1b[32m', `writing ${url}... \n`); + fs.writeFileSync(filePath, html); + } + + await browser.close(); + process.exit(); +})(); diff --git a/tdesign-site/script/qrcode/api.js b/tdesign-site/script/qrcode/api.js new file mode 100644 index 000000000..def788bb0 --- /dev/null +++ b/tdesign-site/script/qrcode/api.js @@ -0,0 +1,35 @@ +const { get, post } = require('./httpRequest'); + +/** + * 获取access_token, 网页调试工具:https://mp.weixin.qq.com/debug/cgi-bin/apiinfo?t=index&type=%E5%9F%BA%E7%A1%80%E6%94%AF%E6%8C%81&form=%E8%8E%B7%E5%8F%96access_token%E6%8E%A5%E5%8F%A3%20/token&token=&lang=zh_CN + * @api https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET + * @method GET + * @parameter grant_type + * @parameter appid + * @parameter secret + * @return Object + */ +const getAccessToken = (appId, appSecret) => { + const url = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}`; + return get(url); +}; + +/** + * 获取小程序码,适用于需要的码数量极多的业务场景。通过该接口生成的小程序码,永久有效,数量暂无限制。 + * @api https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN + * @method POST + * @parameter access_token + * @parameter body + * @parameter scene //跳转带参 + * @parameter path //跳转页面 + * @return 二成功时返回的是 Buffer ,失败时返回 JSON + */ +const getUnlimitedQRCode = (token, parameter, config) => { + const url = `https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=${token}`; + return post(url, parameter, config); +}; + +module.exports = { + getAccessToken, + getUnlimitedQRCode, +}; diff --git a/tdesign-site/script/qrcode/httpRequest.js b/tdesign-site/script/qrcode/httpRequest.js new file mode 100644 index 000000000..b54961b2d --- /dev/null +++ b/tdesign-site/script/qrcode/httpRequest.js @@ -0,0 +1,74 @@ +/** axios封装 + * 请求拦截、响应拦截、错误统一处理 + */ +const axios = require('axios'); + +// axios.defaults.timeout = 10000; // 超时抛出异常 +// axios.defaults.withCredentials = true; +axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'; // post请求头 +axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; // 默认异步请求 + +// 请求拦截 +axios.interceptors.request.use( + (config) => config, + (error) => Promise.error(error), +); +// 响应拦截 +axios.interceptors.response.use( + (response) => { + if (response.status === 200) { + return Promise.resolve(response); + } + return Promise.reject(response); + }, + // 服务器状态码不是200的情况 + (error) => { + if (error.response.status) { + return Promise.reject(error.response); + } + }, +); + +/** + * @description get请求 + * @parameter {String} url [请求的url地址] + * @parameter {Object} parameter [请求时携带的参数] + * @parameter {Object} config [其他配置信息] + */ +const get = (url, parameter, config) => { + return new Promise((resolve, reject) => { + axios + .get(url, parameter, config) + .then((res) => { + resolve(res.data); + }) + .catch((err) => { + reject(err.data); + }); + }); +}; + +/** + * @description post请求 + * @parameter {String} url [请求的url地址] + * @parameter {Object} parameter [请求时携带的参数] + * @parameter {Object} config [其他配置信息] + */ + +const post = (url, parameter, config) => { + return new Promise((resolve, reject) => { + axios + .post(url, parameter, config) + .then((res) => { + resolve(res.data); + }) + .catch((err) => { + reject(err.data); + }); + }); +}; + +module.exports = { + get, + post, +}; diff --git a/tdesign-site/script/qrcode/index.js b/tdesign-site/script/qrcode/index.js new file mode 100644 index 000000000..55baf47f0 --- /dev/null +++ b/tdesign-site/script/qrcode/index.js @@ -0,0 +1,107 @@ +const fs = require('fs'); +const path = require('path'); +const { getAccessToken, getUnlimitedQRCode } = require('./api'); + +const APP_ID = process.argv[process.argv.indexOf('--APP_ID') + 1]; // 在 --APP_ID 后面 +const APP_SECRET = process.argv[process.argv.indexOf('--APP_SECRET') + 1]; // --APP_SECRET 后面 + +// 去读 app.json 中 pages && subpackages 字段 +const { pages, subpackages } = require('../../example/app.json'); + +const isExistStr = (str, arr) => { + const temp = [...new Set(str.split('/').slice(1))].join('-'); + return arr.includes(temp); +}; + +const getImageList = () => { + const imageFolderDir = path.resolve(__dirname, `../../site/public/assets/qrcode`); + const images = fs.readdirSync(imageFolderDir); + const imageOldList = []; + images.forEach((item) => { + imageOldList.push(item.split('.')[0]); + }); + return imageOldList; +}; + +const getNewPageList = (list) => { + const pageList = []; + const imageOldList = getImageList(); + list.forEach((item) => { + if (Object.prototype.toString.call(item) === '[object Object]') { + item.pages.forEach((subItem) => { + if (!isExistStr(item.root + subItem, imageOldList)) { + pageList.push(item.root + subItem); + } + }); + } else if (!isExistStr(item, imageOldList)) { + pageList.push(item); + } + }); + + return pageList; +}; + +const getUnlimitedQRCodeImage = (appid, appSecret) => { + getAccessToken(appid, appSecret).then((e) => { + if (e.access_token) { + const token = e.access_token; + // eslint-disable-next-line no-console + console.log('==access_token 2h内有效=', token); + const baseParameter = { + width: 280, // 小程序码大小 + // check_path: false, + }; + const baseConfig = { + responseType: 'arraybuffer', + }; + + const pageList = getNewPageList(pages.concat(subpackages)); + + // 循环 pages, 获取相应小程序码 + pageList.forEach((item, index) => { + const temp = [...new Set(item.split('/').slice(1))]; + const fileName = temp.join('-'); + + const specialParameter = { + page: item, // 扫码进入的小程序页面路径 + scene: `name=${temp[0]}`, // 标识 + }; + getUnlimitedQRCode(token, JSON.stringify({ ...specialParameter, ...baseParameter }), { ...baseConfig }).then( + (res) => { + // 因为微信接口 getwxacodeunlimit 成功时返回的是 Buffer ,失败时返回 JSON 结构。 + // 这里把返回数据全部当成 Buffer 处理,所以 res.length < 200, 则表示获取失败。 + if (res.length < 200) { + const { errcode, errmsg } = JSON.parse(res.toString()); + // eslint-disable-next-line no-console + console.log('===小程序码获取失败===', item, { errcode, errmsg }); + return; + } + + const buffer = Buffer.from(res, 'base64'); + const destPath = path.resolve(__dirname, `../../site/public/assets/qrcode/${fileName}.png`); + + fs.writeFile( + destPath, + buffer, + { + encoding: 'binary', + flag: 'w+', + }, + (err) => { + if (err) { + // eslint-disable-next-line no-console + console.log('===小程序码图片存储错误===', err); + } + }, + ); + }, + ); + }); + } + }); +}; + +/** + * @description 命令行生成小程序码 npm run qrcode -- --APP_ID xxx --APP_SECRET xxx + */ +getUnlimitedQRCodeImage(APP_ID, APP_SECRET); diff --git a/tdesign-site/script/style.js b/tdesign-site/script/style.js new file mode 100644 index 000000000..7f271b40e --- /dev/null +++ b/tdesign-site/script/style.js @@ -0,0 +1,25 @@ +const glob = require('glob'); +const fs = require('fs'); +const path = require('path'); + +glob('src/**/*.md', (err, files) => { + if (err) { + console.log(err); + } + + const handler = (file) => { + const filePath = path.resolve(__dirname, '..', file); + const content = fs.readFileSync(filePath, { encoding: 'utf8' }); + const splitContent = content.split('\n'); + + splitContent.forEach((content, index) => { + if (/custom-style/.test(content)) { + splitContent.splice(index, 1); + } + }); + + fs.writeFileSync(filePath, splitContent.join('\n')); + console.log(filePath); + }; + files.forEach(handler); +}); diff --git a/tdesign-site/script/test/globalSetup.js b/tdesign-site/script/test/globalSetup.js new file mode 100644 index 000000000..281116891 --- /dev/null +++ b/tdesign-site/script/test/globalSetup.js @@ -0,0 +1,3 @@ +module.exports = async () => { + process.env.TZ = 'Asia/Shanghai'; +}; diff --git a/tdesign-site/script/test/setup.js b/tdesign-site/script/test/setup.js new file mode 100644 index 000000000..af8a50d11 --- /dev/null +++ b/tdesign-site/script/test/setup.js @@ -0,0 +1,17 @@ +import Path from 'path'; +import simulate from 'miniprogram-simulate'; +import { canUseVirtualHost } from '../../src/common/version'; + +global.getApp = () => null; +global.Page = (options) => Component(options); +global.load = (path, demoName) => { + return simulate.load(path, demoName, { + less: true, + rootPath: Path.resolve(__dirname, '../../src'), + compilerOptions: { + maxBuffer: 1024 * 1024 * 2, + }, + }); +}; + +global.VIRTUAL_HOST = canUseVirtualHost(); diff --git a/tdesign-site/script/test/snapshotResolver.js b/tdesign-site/script/test/snapshotResolver.js new file mode 100644 index 000000000..47664f502 --- /dev/null +++ b/tdesign-site/script/test/snapshotResolver.js @@ -0,0 +1,12 @@ +module.exports = { + // resolves from test to snapshot path + resolveSnapshotPath: (testPath, snapshotExtension) => + testPath.replace('__test__', '__test__/__virtualHostSnapshot__') + snapshotExtension, + + // resolves from snapshot to test path + resolveTestPath: (snapshotFilePath, snapshotExtension) => + snapshotFilePath.replace('__test__/__virtualHostSnapshot__', '__test__').slice(0, -snapshotExtension.length), + + // Example test path, used for preflight consistency check of the implementation above + testPathForConsistencyCheck: 'some/__tests__/example.test.js', +}; diff --git a/tdesign-site/script/test/transform.js b/tdesign-site/script/test/transform.js new file mode 100644 index 000000000..0ac53ea8f --- /dev/null +++ b/tdesign-site/script/test/transform.js @@ -0,0 +1,15 @@ +// 由于开启 virtualHost=true 之后,selectComponent 无法获取,暂时在单测关闭 virtualHost +const path = require('path'); +const babelJest = require('babel-jest'); + +module.exports = { + process(sourceText, sourcePath) { + if (sourcePath.indexOf('instantiationDecorator') > -1) { + sourceText = sourceText.replace('virtualHost = true', 'virtualHost = false'); + } + + return babelJest.process(sourceText, sourcePath, { + cwd: path.resolve(__dirname, '../../'), + }); + }, +}; diff --git a/tdesign-site/script/test/virtualHostSetup.js b/tdesign-site/script/test/virtualHostSetup.js new file mode 100644 index 000000000..7ddc3fc70 --- /dev/null +++ b/tdesign-site/script/test/virtualHostSetup.js @@ -0,0 +1,43 @@ +import Path from 'path'; +import simulate from 'miniprogram-simulate'; +import similateApi from 'miniprogram-simulate/src/api'; +import { canUseVirtualHost } from '../../src/common/version'; + +global.getApp = () => null; +global.Page = (options) => Component(options); +global.load = (path, demoName) => { + return simulate.load(path, demoName, { + less: true, + rootPath: Path.resolve(__dirname, '../../src'), + compilerOptions: { + maxBuffer: 1024 * 1024 * 2, + }, + }); +}; + +global.wx = { + ...similateApi, + getSystemInfoSync: () => { + return { + // SDKVersion > 2.19.2, 开启 VirtualHost + SDKVersion: '2.20.0', + batteryLevel: 100, + benchmarkLevel: 1, + brand: 'devtools', + fontSizeSetting: 16, + language: 'zh_CN', + model: 'iPhone 7 Plus', + pixelRatio: 3, + platform: 'devtools', + screenHeight: 736, + screenWidth: 414, + statusBarHeight: 20, + system: 'iOS 10.0.1', + version: '6.6.3', + windowHeight: 672, + windowWidth: 414, + }; + }, +}; + +global.VIRTUAL_HOST = canUseVirtualHost(); diff --git a/tdesign-site/script/tpl.json b/tdesign-site/script/tpl.json new file mode 100644 index 000000000..7fb07dbe9 --- /dev/null +++ b/tdesign-site/script/tpl.json @@ -0,0 +1,267 @@ +{ + "t-action-sheet": { + "tpl": "" + }, + "t-avatar-group": { + "tpl": "", + "require": { + "t-avatar": "./avatar/avatar" + }, + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-avatar.png" + }, + "t-avatar": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-avatar.png" + }, + "t-back-top": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-backtop.png" + }, + "t-badge": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-badge.png" + }, + "t-button": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-button.png" + }, + "t-cell": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-cell.png" + }, + "t-cell-group": { + "tpl": "", + "require": { + "t-cell": "./cell/cell" + }, + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-cell.png" + }, + "t-check-tag": { + "tpl": "check tag" + }, + "t-checkbox": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-checkbox.png" + }, + "t-checkbox-group": { + "tpl": "", + "require": { + "t-checkbox": "./checkbox/checkbox" + }, + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-checkbox.png" + }, + "t-collapse-panel": { + "tpl": "此处可自定义内容" + }, + "t-collapse": { + "tpl": "此处可自定义内容", + "require": { + "t-collapse-panel": "./collapse/collapse-panel" + } + }, + "t-count-down": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-countdown.png" + }, + "t-date-time-picker": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-datetimepicker.png" + }, + "t-dialog": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-dialog.png" + }, + "t-divider": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-divider.png" + }, + "t-drawer": { + "tpl": "" + }, + "t-dropdown-item": { + "tpl": "" + }, + "t-dropdown-menu": { + "tpl": "", + "require": { + "t-dropdown-menu": "./dropdown-menu/dropdown-item" + } + }, + "t-empty": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-empty.png" + }, + "t-fab": { + "tpl": "" + }, + "t-footer": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-footer.png" + }, + "t-grid-item": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-grid.png" + }, + "t-grid": { + "tpl": "", + "require": { + "t-grid-item": "./grid/grid-item" + }, + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-grid.png" + }, + "t-icon": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-icon.png" + }, + "t-image": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-image.png" + }, + "t-indexes": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-indexes.png" + }, + "t-input": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-input.png" + }, + "t-loading": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-loading.png" + }, + "t-message": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-message.png" + }, + "t-navbar": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-navbar.png" + }, + "t-picker-item": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-picker.png" + }, + "t-picker": { + "tpl": "", + "require": { + "t-picker-item": "./picker/picker-item" + }, + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-picker.png" + }, + "t-popup": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-popup.png" + }, + "t-progress": { + "tpl": "" + }, + "t-pull-down-refresh": { + "tpl": "拖拽该区域演示 中间下拉刷新", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-pulldownrefresh.png" + }, + "t-radio-group": { + "tpl": "", + "require": { + "t-radio": "./radio/radio" + }, + "icon:": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-radio.png" + }, + "t-radio": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-radio.png" + }, + "t-rate": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-rate.png" + }, + "t-search": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-search.png" + }, + "t-skeleton": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-skeleton.png" + }, + "t-slider": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-slider.png" + }, + "t-stepper": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-stepper.png" + }, + "t-step-item": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-steps.png" + }, + "t-steps": { + "tpl": "", + "require": { + "t-step-item": "./steps/step-item" + }, + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-steps.png" + }, + "t-sticky": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-sticky.png" + }, + "t-swipe-cell": { + "tpl": "删除", + "require": { + "t-cell": "./cell/cell" + }, + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-swipecell.png" + }, + "t-swiper": { + "tpl": "", + "require": { + "t-swiper-item": "./swiper/swiper-item" + }, + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-swiper.png" + }, + "t-switch": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-switch.png" + }, + "t-tab-bar-item": { + "tpl": "{{item.label}}", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-tabbar.png" + }, + "t-tab-bar": { + "tpl": "{{item.label}}", + "require": { + "t-tab-bar-item": "./tab-bar/tab-bar-item" + }, + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-tabbar.png" + }, + "t-tab-panel": { + "tpl": "标签一内容", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-tabs.png" + }, + "t-tabs": { + "tpl": "标签一内容标签二内容", + "require": { + "t-tab-panel": "./tabs/tab-panel" + }, + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-tabs.png" + }, + "t-tag": { + "tpl": "重要", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-tag.png" + }, + "t-textarea": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-textarea.png" + }, + "t-toast": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-toast.png" + }, + "t-transition": { + "tpl": "" + }, + "t-upload": { + "tpl": "", + "icon": "https://tdesign.gtimg.com/site/miniprogram-doc/doc-upload.png" + } +} diff --git a/tdesign-site/script/update-icons.js b/tdesign-site/script/update-icons.js new file mode 100644 index 000000000..c2deb44c1 --- /dev/null +++ b/tdesign-site/script/update-icons.js @@ -0,0 +1,21 @@ +const path = require('path'); +const fs = require('fs'); + +const iconFile = path.join(__dirname, '..', 'src/icon/icon.less'); +const dataFile = path.join(__dirname, '..', 'src/icon/_example/data.js'); +fs.readFile(iconFile, 'utf8', (err, data) => { + if (err) { + console.error(err); + return; + } + const regex = new RegExp(`\\..*-icon-(.*):before`, 'g'); + const icons = data.match(regex); + const iconList = icons.map((icon) => icon.replace(regex, '$1')); + const iconsData = `const icons = ${JSON.stringify(iconList, null, 2)}; \nexport default icons;\n`; + fs.writeFile(dataFile, iconsData, (err) => { + if (err) { + console.error(err); + } + }); + console.log('update icons success!'); +}); diff --git a/tdesign-site/site/README.md b/tdesign-site/site/README.md new file mode 100644 index 000000000..d84251367 --- /dev/null +++ b/tdesign-site/site/README.md @@ -0,0 +1,7 @@ + +## Flutter官网运行调试文档 + +- 进入 `site` 文件夹,安装`npm install` 安装依赖包 +- `cd ..` 回到 `tdesign-site` 目录,运行命令 `npm run site:dev` + +官方文档的内容,是根据组件库项目中的各个 `README.md` 自动生成 diff --git a/tdesign-site/site/app.vue b/tdesign-site/site/app.vue new file mode 100644 index 000000000..ff04d5253 --- /dev/null +++ b/tdesign-site/site/app.vue @@ -0,0 +1,76 @@ + + + diff --git a/tdesign-site/site/components/qrcode.vue b/tdesign-site/site/components/qrcode.vue new file mode 100644 index 000000000..23c4b2ccd --- /dev/null +++ b/tdesign-site/site/components/qrcode.vue @@ -0,0 +1,91 @@ + + + + + \ No newline at end of file diff --git a/tdesign-site/site/data/projects.ts b/tdesign-site/site/data/projects.ts new file mode 100644 index 000000000..3bc0b6970 --- /dev/null +++ b/tdesign-site/site/data/projects.ts @@ -0,0 +1,75 @@ +export default [ + { + name: '未来运动场', + tags: ['趣味游戏'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/weilaiyundongchang.png'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/weilaiyundongchang-code.jpg', + }, + { + name: '程序员做饭指南', + tags: ['效率工具', '开源项目'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/zuofanzhinan.png'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/zuofanzhinan-code.jpg', + url: 'https://github.com/LeeJim/HowToCookOnMiniprogram', + }, + { + name: '李宁', + tags: ['电商'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/lining.jpg'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/lining-code.jpg', + }, + { + name: '美宜家', + tags: ['电商'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/meiyijia.jpg'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/meiyijia-code.jpg', + }, + { + name: '沃尔玛', + tags: ['电商'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/walmart.jpg'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/walmart-code.jpg', + }, + { + name: '维达', + tags: ['电商'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/weida.jpg'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/weida-code.png', + }, + { + name: '小云卡片', + tags: ['效率工具'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/xiaoyunkapian.png'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/xiaoyunkapian-code.jpg', + }, + { + name: '小楼助教', + tags: ['效率工具'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/xiaolouzhujiao.png'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/xiaolouzhujiao-code.png', + }, + { + name: 'frmall', + tags: ['电商', '开源项目'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/frmall.png'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/frmall-code.jpg', + }, + { + name: 'Coding 鱼塘', + tags: ['效率工具'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/codingyutang.png'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/codingyutang-code.jpg', + }, + { + name: '智写 AI', + tags: ['效率工具'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/zhixieai.png'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/zhixieai-code.png', + }, + { + name: '好用二维码', + tags: ['效率工具'], + preview: ['https://tdesign.gtimg.com/miniprogram/case/haoyongqrcode.png'], + qrcode: 'https://tdesign.gtimg.com/miniprogram/case/haoyongqrcode-code.png', + }, +]; diff --git a/tdesign-site/site/docs/custom-style.md b/tdesign-site/site/docs/custom-style.md new file mode 100644 index 000000000..c460c3f1c --- /dev/null +++ b/tdesign-site/site/docs/custom-style.md @@ -0,0 +1,113 @@ +--- +title: 样式覆盖 +description: 微信小程序提供了多种方式可以实现样式覆盖 +spline: explain +--- + +基于微信小程序的设计,TDesign 提供了 4 种方式用于样式覆盖 + +## 1 使用 Style + +> TDesign 全部组件均支持 `custom-style` 和 `style` 属性。可传入 CSS 字符串,将会应用于组件的根元素。 + +### 开启 virtualHost + +从 `1.0.0-rc` 版本开始,在基础库版本高于 2.19.2 的情况下,TDesign 会默认开启 `virtualHost` 属性。 + +此时 `custom-style` 和 `style` 的效果是一致的,任选其一即可: + +```html +填充按钮 + +填充按钮 +``` + +渲染的结果如下: + +```html +