master 90e2cbcbb4a5 cached
354 files
640.9 KB
231.0k tokens
217 symbols
1 requests
Download .txt
Showing preview only (719K chars total). Download the full file or copy to clipboard to get everything.
Repository: wechat-miniprogram/weui-miniprogram
Branch: master
Commit: 90e2cbcbb4a5
Files: 354
Total size: 640.9 KB

Directory structure:
gitextract_c6xlk5rv/

├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── ----.md
│       └── --bug.md
├── .gitignore
├── .gitmodules
├── .npmignore
├── .npmrc
├── .prettierrc.js
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── babel.config.js
├── docs/
│   ├── README.md
│   ├── actionsheet.md
│   ├── badge.md
│   ├── cell.md
│   ├── cells.md
│   ├── checkbox-group.md
│   ├── dialog.md
│   ├── form-page.md
│   ├── form.md
│   ├── gallery.md
│   ├── half-screen-dialog.md
│   ├── icon.md
│   ├── loading.md
│   ├── msg.md
│   ├── navigation.md
│   ├── other.md
│   ├── quickstart.md
│   ├── search.md
│   ├── slideview.md
│   ├── tabbar.md
│   ├── toptips.md
│   └── uploader.md
├── gulpfile.js
├── jest.config.js
├── mpflow.config.js
├── package.json
├── src/
│   ├── app.js
│   ├── app.json
│   ├── app.less
│   ├── base/
│   │   ├── CustomPage.js
│   │   └── behaviors/
│   │       └── theme.js
│   ├── components/
│   │   ├── actionsheet/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── actionsheet.test.ts.snap
│   │   │   │   ├── actionsheet.test.ts
│   │   │   │   ├── index.json
│   │   │   │   ├── index.ts
│   │   │   │   └── index.wxml
│   │   │   ├── actionsheet.json
│   │   │   ├── actionsheet.less
│   │   │   ├── actionsheet.ts
│   │   │   └── actionsheet.wxml
│   │   ├── badge/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── badage.test.js.snap
│   │   │   │   └── badage.test.js
│   │   │   ├── badge.json
│   │   │   ├── badge.less
│   │   │   ├── badge.ts
│   │   │   └── badge.wxml
│   │   ├── cell/
│   │   │   ├── cell.json
│   │   │   ├── cell.less
│   │   │   ├── cell.ts
│   │   │   └── cell.wxml
│   │   ├── cells/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── cells.test.js.snap
│   │   │   │   ├── cells.test.js
│   │   │   │   ├── index.js
│   │   │   │   ├── index.json
│   │   │   │   └── index.wxml
│   │   │   ├── cells.json
│   │   │   ├── cells.less
│   │   │   ├── cells.ts
│   │   │   └── cells.wxml
│   │   ├── checkbox/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── checkbox.test.js.snap
│   │   │   │   ├── checkbox.test.js
│   │   │   │   ├── index.js
│   │   │   │   ├── index.json
│   │   │   │   └── index.wxml
│   │   │   ├── checkbox.json
│   │   │   ├── checkbox.less
│   │   │   ├── checkbox.ts
│   │   │   └── checkbox.wxml
│   │   ├── checkbox-group/
│   │   │   ├── checkbox-group.json
│   │   │   ├── checkbox-group.less
│   │   │   ├── checkbox-group.ts
│   │   │   └── checkbox-group.wxml
│   │   ├── dialog/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── dialog.test.js.snap
│   │   │   │   ├── dialog.test.js
│   │   │   │   ├── index.js
│   │   │   │   ├── index.json
│   │   │   │   └── index.wxml
│   │   │   ├── dialog.json
│   │   │   ├── dialog.less
│   │   │   ├── dialog.ts
│   │   │   └── dialog.wxml
│   │   ├── form/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── form.test.ts.snap
│   │   │   │   ├── comp/
│   │   │   │   │   ├── index.json
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── index.wxml
│   │   │   │   ├── form-validator.test.ts
│   │   │   │   ├── form.test.ts
│   │   │   │   └── validator.test.ts
│   │   │   ├── form-validator.ts
│   │   │   ├── form.json
│   │   │   ├── form.ts
│   │   │   ├── form.wxml
│   │   │   └── validator.ts
│   │   ├── form-page/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── form-page.test.ts.snap
│   │   │   │   ├── form-page.test.ts
│   │   │   │   ├── index.js
│   │   │   │   ├── index.json
│   │   │   │   └── index.wxml
│   │   │   ├── form-page.json
│   │   │   ├── form-page.less
│   │   │   ├── form-page.ts
│   │   │   └── form-page.wxml
│   │   ├── gallery/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── gallery.test.ts.snap
│   │   │   │   └── gallery.test.ts
│   │   │   ├── gallery.json
│   │   │   ├── gallery.less
│   │   │   ├── gallery.ts
│   │   │   └── gallery.wxml
│   │   ├── grids/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── grid.test.ts.snap
│   │   │   │   └── grid.test.ts
│   │   │   ├── grids.json
│   │   │   ├── grids.less
│   │   │   ├── grids.ts
│   │   │   └── grids.wxml
│   │   ├── half-screen-dialog/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── half-screen-dialog.test.ts.snap
│   │   │   │   └── half-screen-dialog.test.ts
│   │   │   ├── half-screen-dialog.json
│   │   │   ├── half-screen-dialog.less
│   │   │   ├── half-screen-dialog.ts
│   │   │   └── half-screen-dialog.wxml
│   │   ├── icon/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── icon.test.ts.snap
│   │   │   │   └── icon.test.ts
│   │   │   ├── base64.ts
│   │   │   ├── icon.json
│   │   │   ├── icon.less
│   │   │   ├── icon.ts
│   │   │   ├── icon.wxml
│   │   │   └── icondata.ts
│   │   ├── index.json
│   │   ├── index.less
│   │   ├── index.ts
│   │   ├── index.wxml
│   │   ├── loading/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── loading.test.ts.snap
│   │   │   │   └── loading.test.ts
│   │   │   ├── loading.json
│   │   │   ├── loading.less
│   │   │   ├── loading.ts
│   │   │   └── loading.wxml
│   │   ├── msg/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── msg.test.ts.snap
│   │   │   │   └── msg.test.ts
│   │   │   ├── msg.json
│   │   │   ├── msg.less
│   │   │   ├── msg.ts
│   │   │   └── msg.wxml
│   │   ├── navigation-bar/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── navigation-bar.test.ts.snap
│   │   │   │   └── navigation-bar.test.ts
│   │   │   ├── navigation-bar.json
│   │   │   ├── navigation-bar.less
│   │   │   ├── navigation-bar.ts
│   │   │   └── navigation-bar.wxml
│   │   ├── patch.less
│   │   ├── searchbar/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── searchbar.test.ts.snap
│   │   │   │   └── searchbar.test.ts
│   │   │   ├── searchbar.json
│   │   │   ├── searchbar.less
│   │   │   ├── searchbar.ts
│   │   │   └── searchbar.wxml
│   │   ├── slideview/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── slideview.test.ts.snap
│   │   │   │   └── slideview.test.ts
│   │   │   ├── slideview-skyline.json
│   │   │   ├── slideview-skyline.less
│   │   │   ├── slideview-skyline.ts
│   │   │   ├── slideview-skyline.wxml
│   │   │   ├── slideview.json
│   │   │   ├── slideview.less
│   │   │   ├── slideview.ts
│   │   │   ├── slideview.wxml
│   │   │   └── slideview.wxs
│   │   ├── tabbar/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── tabbar.test.ts.snap
│   │   │   │   └── tabbar.test.ts
│   │   │   ├── tabbar.json
│   │   │   ├── tabbar.less
│   │   │   ├── tabbar.ts
│   │   │   └── tabbar.wxml
│   │   ├── toptips/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── toptips.test.ts.snap
│   │   │   │   └── toptips.test.ts
│   │   │   ├── toptips.json
│   │   │   ├── toptips.less
│   │   │   ├── toptips.ts
│   │   │   └── toptips.wxml
│   │   ├── uploader/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── uploader.test.ts.snap
│   │   │   │   └── uploader.test.ts
│   │   │   ├── uploader.json
│   │   │   ├── uploader.less
│   │   │   ├── uploader.ts
│   │   │   └── uploader.wxml
│   │   └── utils/
│   │       ├── object.ts
│   │       ├── string.ts
│   │       └── utils.less
│   ├── example/
│   │   ├── actionsheet/
│   │   │   ├── actionsheet.js
│   │   │   ├── actionsheet.json
│   │   │   ├── actionsheet.wxml
│   │   │   └── actionsheet.wxss
│   │   ├── article/
│   │   │   ├── article.js
│   │   │   ├── article.json
│   │   │   ├── article.wxml
│   │   │   └── article.wxss
│   │   ├── badge/
│   │   │   ├── badge.js
│   │   │   ├── badge.json
│   │   │   ├── badge.wxml
│   │   │   └── badge.wxss
│   │   ├── button/
│   │   │   ├── button.js
│   │   │   ├── button.json
│   │   │   ├── button.wxml
│   │   │   └── button.wxss
│   │   ├── cell/
│   │   │   ├── cell.js
│   │   │   ├── cell.json
│   │   │   ├── cell.wxml
│   │   │   └── cell.wxss
│   │   ├── common.less
│   │   ├── dialog/
│   │   │   ├── dialog.js
│   │   │   ├── dialog.json
│   │   │   ├── dialog.wxml
│   │   │   └── dialog.wxss
│   │   ├── flex/
│   │   │   ├── flex.js
│   │   │   ├── flex.json
│   │   │   ├── flex.wxml
│   │   │   └── flex.wxss
│   │   ├── footer/
│   │   │   ├── footer.js
│   │   │   ├── footer.json
│   │   │   ├── footer.wxml
│   │   │   └── footer.wxss
│   │   ├── form/
│   │   │   ├── form.js
│   │   │   ├── form.json
│   │   │   ├── form.wxml
│   │   │   └── form.wxss
│   │   ├── form-page/
│   │   │   ├── form-page.js
│   │   │   ├── form-page.json
│   │   │   ├── form-page.wxml
│   │   │   └── form-page.wxss
│   │   ├── gallery/
│   │   │   ├── gallery.js
│   │   │   ├── gallery.json
│   │   │   ├── gallery.wxml
│   │   │   └── gallery.wxss
│   │   ├── grid/
│   │   │   ├── grid.js
│   │   │   ├── grid.json
│   │   │   ├── grid.wxml
│   │   │   └── grid.wxss
│   │   ├── half-screen-dialog/
│   │   │   ├── half-screen-dialog.js
│   │   │   ├── half-screen-dialog.json
│   │   │   ├── half-screen-dialog.wxml
│   │   │   └── half-screen-dialog.wxss
│   │   ├── icons/
│   │   │   ├── icons.js
│   │   │   ├── icons.json
│   │   │   ├── icons.wxml
│   │   │   └── icons.wxss
│   │   ├── images/
│   │   │   └── base64.js
│   │   ├── index.js
│   │   ├── index.json
│   │   ├── index.less
│   │   ├── index.wxml
│   │   ├── loading/
│   │   │   ├── loading.js
│   │   │   ├── loading.json
│   │   │   ├── loading.wxml
│   │   │   └── loading.wxss
│   │   ├── loadmore/
│   │   │   ├── loadmore.js
│   │   │   ├── loadmore.json
│   │   │   ├── loadmore.wxml
│   │   │   └── loadmore.wxss
│   │   ├── msg/
│   │   │   ├── msg.js
│   │   │   ├── msg.json
│   │   │   ├── msg.wxml
│   │   │   ├── msg.wxss
│   │   │   ├── msg_fail.js
│   │   │   ├── msg_fail.json
│   │   │   ├── msg_fail.wxml
│   │   │   ├── msg_fail.wxss
│   │   │   ├── msg_success.js
│   │   │   ├── msg_success.json
│   │   │   ├── msg_success.wxml
│   │   │   ├── msg_success.wxss
│   │   │   ├── msg_text.js
│   │   │   ├── msg_text.json
│   │   │   ├── msg_text.wxml
│   │   │   ├── msg_text.wxss
│   │   │   ├── msg_text_primary.js
│   │   │   ├── msg_text_primary.json
│   │   │   ├── msg_text_primary.wxml
│   │   │   └── msg_text_primary.wxss
│   │   ├── navbar/
│   │   │   ├── navbar.js
│   │   │   ├── navbar.json
│   │   │   ├── navbar.wxml
│   │   │   └── navbar.wxss
│   │   ├── navigation/
│   │   │   ├── navigation.js
│   │   │   ├── navigation.json
│   │   │   ├── navigation.wxml
│   │   │   └── navigation.wxss
│   │   ├── panel/
│   │   │   ├── panel.js
│   │   │   ├── panel.json
│   │   │   ├── panel.wxml
│   │   │   └── panel.wxss
│   │   ├── picker/
│   │   │   ├── picker.js
│   │   │   ├── picker.json
│   │   │   ├── picker.wxml
│   │   │   └── picker.wxss
│   │   ├── preview/
│   │   │   ├── preview.js
│   │   │   ├── preview.json
│   │   │   ├── preview.wxml
│   │   │   └── preview.wxss
│   │   ├── progress/
│   │   │   ├── progress.js
│   │   │   ├── progress.json
│   │   │   ├── progress.wxml
│   │   │   └── progress.wxss
│   │   ├── searchbar/
│   │   │   ├── searchbar.js
│   │   │   ├── searchbar.json
│   │   │   ├── searchbar.wxml
│   │   │   └── searchbar.wxss
│   │   ├── slider/
│   │   │   ├── slider.js
│   │   │   ├── slider.json
│   │   │   ├── slider.wxml
│   │   │   └── slider.wxss
│   │   ├── slideview/
│   │   │   ├── slideview.js
│   │   │   ├── slideview.json
│   │   │   ├── slideview.wxml
│   │   │   └── slideview.wxss
│   │   ├── tabbar/
│   │   │   ├── tabbar.js
│   │   │   ├── tabbar.json
│   │   │   ├── tabbar.wxml
│   │   │   └── tabbar.wxss
│   │   ├── toast/
│   │   │   ├── toast.js
│   │   │   ├── toast.json
│   │   │   ├── toast.wxml
│   │   │   └── toast.wxss
│   │   ├── toptips/
│   │   │   ├── toptips.js
│   │   │   ├── toptips.json
│   │   │   ├── toptips.wxml
│   │   │   └── toptips.wxss
│   │   └── uploader/
│   │       ├── uploader.js
│   │       ├── uploader.json
│   │       ├── uploader.wxml
│   │       └── uploader.wxss
│   └── project.config.json
├── tools/
│   ├── build.js
│   ├── checkcomponents.js
│   ├── checkwxss.js
│   ├── config.js
│   ├── package.json
│   └── utils.js
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .eslintignore
================================================
node_modules
src/weui-wxss
miniprogram_dist
miniprogram_dev
tools

================================================
FILE: .eslintrc.js
================================================
module.exports = {
    root: true,
    parser: '@typescript-eslint/parser',
    parserOptions: {
        ecmaVersion: 6,
        sourceType: 'module'
    },
    env: {
        es6: true,
        browser: true,
        jest: true,
        commonjs: true,
        node: true
    },
    plugins: [
      '@typescript-eslint',
      'prettier'
    ],
    extends: [
        'eslint:recommended',
        'plugin:@typescript-eslint/eslint-recommended',
        'plugin:@typescript-eslint/recommended',
        'prettier/@typescript-eslint'
    ],
    globals: {
        wx: true,
        App: true,
        Page: true,
        Component: true
    },
    rules: {
        'prettier/prettier': 'error',
        'no-console': ["error", { allow: ["warn", "error"] } ],
        '@typescript-eslint/ban-ts-ignore': 'off',
        '@typescript-eslint/no-empty-function': 'off',
        '@typescript-eslint/explicit-function-return-type': 'off',
        '@typescript-eslint/no-explicit-any': 'off'
    }
}


================================================
FILE: .gitattributes
================================================
* text=auto


================================================
FILE: .github/ISSUE_TEMPLATE/----.md
================================================
---
name: 特性请求
about: 提出一个好的特性
title: "[FEATURE]"
labels: ''
assignees: ''

---

## 遇到的问题
描述你想要达到的效果,以及你的使用场景

## 特性方案
描述你希望的实现方案


================================================
FILE: .github/ISSUE_TEMPLATE/--bug.md
================================================
---
name: 报告Bug
about: 创建一个Bug报告
title: "[BUG]"
labels: ''
assignees: ''

---

## Bug描述
描述你遇到的Bug,所产生的非预期行为

## 复现方式
复现该Bug的方式,建议直接提供 [代码片段](https://developers.weixin.qq.com/miniprogram/dev/devtools/minicode.html)

## 版本信息
- weui 版本 [useExtendedLib 引入则直接填写 useExtendedLib] :


================================================
FILE: .gitignore
================================================
.idea
.DS_Store

logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

miniprogram_dist
miniprogram_dev
node_modules
coverage
dist


================================================
FILE: .gitmodules
================================================


================================================
FILE: .npmignore
================================================
.idea
.DS_Store
package-lock.json

logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

test
tools
docs
miniprogram_dev
node_modules
coverage
tsconfig.json
tslint.json

================================================
FILE: .npmrc
================================================
registry=https://registry.npmjs.org


================================================
FILE: .prettierrc.js
================================================
module.exports = {
  // 一行最多 100 字符
  printWidth: 100,
  // 使用 4 个空格缩进
  tabWidth: 4,
  // 不使用缩进符,而使用空格
  useTabs: false,
  // 行尾需要有分号
  semi: false,
  // 使用单引号
  singleQuote: true,
  // 对象的 key 仅在必要时用引号
  quoteProps: 'as-needed',
  // 末尾不需要逗号
  trailingComma: 'none',
  // 大括号内的首尾需要空格
  bracketSpacing: true,
  // 箭头函数,只有一个参数的时候,也需要括号
  arrowParens: 'always',
  // 每个文件格式化的范围是文件的全部内容
  rangeStart: 0,
  rangeEnd: Infinity,
  // 不需要写文件开头的 @prettier
  requirePragma: false,
  // 不需要自动在文件开头插入 @prettier
  insertPragma: false,
  // 使用默认的折行标准
  proseWrap: 'preserve',
  // 根据显示样式决定 html 要不要折行
  htmlWhitespaceSensitivity: 'css',
  // 换行符使用 lf
  endOfLine: 'lf'
};

================================================
FILE: .travis.yml
================================================
language: node_js
node_js:
  - "v12.16.3"
script: npm run test
after_script:
  - npm run codecov


================================================
FILE: CHANGELOG.md
================================================
# 更新日志

## 1.5.6

- fix: 修改 ext-class 实现
- fix: 修复 checkbox 样式错误

## 1.5.5

- fix: 修复 cell 丢失边缘线样式
- fix: 修复 checkbox 样式错误
- fix: 修复 navigation-bar background 没生效

## 1.5.4

- feat: half-screen-dialog, dialog, actionsheet, gallery, toptips 添加 rootPortal 参数
- fix: 修复 half-screen-dialog, dialog, actionsheet, gallery, toptips 默认不使用 root-portal

## 1.5.3

- fix: 修复 slideview 拆分 skyline 实现

## 1.5.2

- feat: 升级 `weui-wxss` 至 2.6.17
- feat: slideview 拆分 skyline 实现

## 1.5.1

- feat: 修复 dark-mode 实现

## 1.5.0

- feat: 重构 skyline-patch
- feat: 修改 ext-class 实现

## 1.4.2

- fix: 修复 half-screen-dialog skyline 动画
- fix: 修复 navigation-bar 样式

## 1.4.1

- fix: 修复 half-screen-dialog skyline 动画

## 1.4.0

- feat: 正式支持 skyline

## 1.3.1

- fix: 移除 babel 依赖

## 1.3.0

- feat: skyline 兼容
- feat: 升级 `weui-wxss` 至 2.6.6

## 1.2.10

- fix: half-screen-dialog 恢复滚动

## 1.2.9

- fix: dialog 修复 z-index 层级

## 1.2.8

- fix: searchbar 修复聚焦状态

## 1.2.7

- fix: dialog 修复自动聚焦 outline #216
- fix: half-screen-dialog 修复自动聚焦 outline #216
- fix: actionsheet 修复自动聚焦 outline #216
- fix: gallery 修复自动聚焦 outline #216

## 1.2.6

- feat: dialog 使用 virtualHost
- feat: 升级 `weui-wxss` 至 2.5.15

## 1.2.5

- feat: checkbox 添加 disabled 属性
- fix: tabbar 修复 aria-label 告警 #206
- fix: slideview 修复 disabled 属性 #203
- fix: actionsheet 移除多余 wrap
- fix: dialog 移除多余 wrap

## 1.2.4

- feat: 升级 `weui-wxss` 至 2.5.14
- fix: 修复 fade 动画

## 1.2.3

- fix: 修复 half-screen-dialog button 样式向前兼容

## 1.2.2

- feat: tabbar 添加 reactive 选项
- fix: 修复 actionsheet 底部 safearea 高度不正确
- fix: 修复 gallery 底部 safearea 高度不正确
## 1.2.1

- fix: 修复 checkbox 无法操作

## 1.2.0

- feat: 升级 `weui-wxss` 至 2.5.0
- feat: 无障碍适配
- fix: 修复 slideview 隐藏按钮后无动画
- fix: 修复 checkbox-group wx:if 判断不正确
## 1.1.1

- fix: 修复找不到 wxs
- fix: 修复找不到 weui-wxss

## 1.1.0

- feat: 升级 `weui-wxss` 至 2.4.4
- fix: 防止 `mp-half-screen-dialog` 滚动穿透
- chore: 构建系统迁移至 mpflow

## 1.0.8

- fix: 修复 #153
- fix: 修复 `mp-searchbar` 点击取消时错误 focus
- fix: 修复 `mp-half-screen-dialog` 按钮在部分机型按钮排列出错

## 1.0.7

- fix: 修复 `mp-icon` 在 type 改变时报错

## 1.0.6

- feat: `mp-grids` 支持动态修改 grids
- feat: `mp-searchbar` 点击取消按钮时触发 `cancel` 事件
- fix: 修复 `mp-half-screen-dialog` 中的 desc 属性判断
- fix: 修复 `mp-searchbar` 节流搜索没有获取最新数据
- fix: 修复 `form-validator` 中 bytelength 报错

## 1.0.5

- updata: 升级 weui-wxss
- update:使用 `hover-class` 代替 `:active`

## 1.0.4

- fix: 修复 rangelength 校验
- feat: 支持自适应 tabbar

## 1.0.3

- fix: `required` 判断 (#93)
- fix: dialog 未居中 (#94)

## 1.0.2

- fix: Toptips 不展示的bug
- fix: Form 校验失效的bug

## 1.0.1

- 补充 package.json repository

## 1.0.0

- 更新`weui-wxss`到`2.3.0`版本,支持 DarkMode
- Demo 支持 DarkMode

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2019 cunjinli,rockhou,xushengni,tomylin

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.


================================================
FILE: README.md
================================================
## WeUI组件库简介

[![](https://img.shields.io/npm/v/weui-miniprogram.svg?style=flat-square)](https://www.npmjs.com/package/weui-miniprogram)
[![](https://img.shields.io/npm/dw/weui-miniprogram?style=flat-square)](https://www.npmjs.com/package/weui-miniprogram)
[![](https://img.shields.io/travis/wechat-miniprogram/weui-miniprogram.svg?style=flat-square)](https://github.com/wechat-miniprogram/weui-miniprogram)
[![](https://img.shields.io/github/license/wechat-miniprogram/weui-miniprogram.svg?style=flat-square)](https://github.com/wechat-miniprogram/weui-miniprogram)
[![](https://img.shields.io/codecov/c/github/wechat-miniprogram/weui-miniprogram.svg?style=flat-square)](https://github.com/wechat-miniprogram/weui-miniprogram)

这是一套基于样式库[weui-wxss](https://github.com/Tencent/weui-wxss/)开发的小程序扩展组件库,同微信原生视觉体验一致的扩展组件库,由微信官方设计团队和小程序团队为微信小程序量身设计,令用户的使用感知更加统一。

## 如何使用
详细使用参考[文档](https://wechat-miniprogram.github.io/weui/docs/)

## 开发
1. 初始化
```
npm run init
```

2. 执行命令:

```
npm run dev
```

3. 构建组件库:

```
npm run build
```

## 适配 DarkMode

在根结点(或组件的外层结点)增加属性 `data-weui-theme="dark"`,即把 WeUI 组件切换到 DarkMode 的表现,如:

```html
<view data-weui-theme="dark">
    ...
</view>
```


================================================
FILE: babel.config.js
================================================
module.exports = {
    presets: ['@mpflow/plugin-babel/preset', '@mpflow/plugin-typescript/preset']
}


================================================
FILE: docs/README.md
================================================
## WeUI组件库简介
这是一套基于样式库[weui-wxss](https://github.com/Tencent/weui-wxss/)开发的小程序扩展组件库,同微信原生视觉体验一致的UI组件库,由微信官方设计团队和小程序团队为微信小程序量身设计,令用户的使用感知更加统一。

> 支持[扩展库](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#useExtendedLib)引入,不占用小程序包体积。

## 项目背景
随着小程序的普及,微信也有很多内部小程序在开发,每个小程序都需要从零到1进行开发设计,而这个过程中,有大量的UI交互是重复的,另外,微信内部已经有一套H5版本的WeUI样式库。综合考虑,我们基于WeUI样式库开发了小程序版本的UI组件库,在内部多个小程序项目已经使用OK的情况下,我们把这套组件库开源让外部开发者也可以使用,欢迎大家Star以及提Issue。

## 快速上手
请参考[快速上手](./quickstart.md)

## 在线预览
扫描下方小程序码可以预览该组件库

![](./img/demo.png#width:300px)

## 参与贡献
欢迎给我们提出宝贵见的意见,[项目地址](https://github.com/wechat-miniprogram/weui-miniprogram)


================================================
FILE: docs/actionsheet.md
================================================
# ActionSheet
底部弹起的操作按钮组件

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-actionSheet": "weui-miniprogram/actionsheet/actionsheet"
  }
}
```

## 示例代码
```html
<!--WXML示例代码-->
<mp-actionSheet bindactiontap="btnClick" show="{{showActionsheet}}" actions="{{groups}}" title="这是一个标题,可以为一行或者两行。">
</mp-actionSheet>
```

```js
Page({
    data: {
        showActionsheet: false,
        groups: [
            { text: '示例菜单', value: 1 },
            { text: '示例菜单', value: 2 },
            { text: '负向菜单', type: 'warn', value: 3 }
        ]
    },
    close: function () {
        this.setData({
            showActionsheet: false
        })
    },
    btnClick(e) {
        console.log(e)
        this.close()
    }
})
```
## 效果展示
![](./img/actionsheet.png#width:300px)

## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| title | string |  | 否 | 标题 |
| show-cancel | boolean | true | 否 | 是否显示取消按钮 |
| cancel-text | string |  | 否 | 取消按钮的文本 |
| mask-class | string |  | 否 | 背景蒙层的class |
| ext-class | string |  | 否 | ActionSheet额外的class |
| mask-closable | boolean | true | 否 | 点击背景蒙层是否可以关闭ActionSheet |
| mask | boolean | true | 否 | 是否显示背景蒙层 |
| show | boolean | false | 否 | 是否显示ActionSheet |
| actions | Array | false | 是 | ActionSheet的按钮项的配置,每一项是包含text、value、type的Object,type的取值为warn和default,表示两种样式 |
| bindclose | eventhandler |  | 否 | 点击背后的mask关闭掉ActionSheet触发的事件 |
| bindactiontap | eventhandler |  | 否 | 点击ActionSheet的按钮项触发的事件,detail为{ value, index } |

## Slot
| 名称 | 描述 |
| ---- | ---- |
| title | 标题区域slot |

================================================
FILE: docs/badge.md
================================================
# Badge徽章
出现在按钮、图标附近的数字或者状态标记。

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-badge": "weui-miniprogram/badge/badge"
  }
}
```

## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| extClass | string |  | 否 | 组件类名 |
| content | string |  | 否 | 内容区域 |

## 提示
不设置 content 属性时,默认展示小圆点 

================================================
FILE: docs/cell.md
================================================
# Cell
Cell是列表或者是表单的一项,常用于设置页的展示,或者用在表单中,作为表单的每一个要填写的项,Cell必须要放在[Cells](./cells.md)组件的下面。

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-cells": "weui-miniprogram/cells/cells",
    "mp-cell": "weui-miniprogram/cell/cell"
  }
}
```

## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| icon | string |  | 否 | Cell的最左侧的icon,是本地图片的路径,icon和名为icon的slot互斥 |
| title | string |  | 否 | Cell最左侧的标题,一般用在[Form](./form.md)表单组件里面,icon和title二选一,title和名为title的slot互斥 |
| hover | boolean | false | 否 | 是否有hover效果 |
| link | boolean | false | 否 | 右侧是有跳转的箭头,`v1.0`后的版本,`link: true` 会自带 hover 效果 |
| value | string |  | 是 | Cell的值,和默认的slot互斥 |
| show-error | boolean | false | 否 | 用在[Form](./form.md)表单组件里面,在表单校验出错的时候是否把Cell标为warn状态 |
| prop | string |  | 否 | 用在[Form](./form.md)表单组件里面,需要校验的表单的字段名 |
| footer | string |  | 否 | Cell右侧区域的内容,和名为footer的slot互斥 |
| inline | boolean |  | 否 | 用在[Form](./form.md)表单组件里面,表示表单项是左右显示还是上下显示 |

## Slot
| 名称 | 描述 |
| ---- | ---- |
| icon | 左侧图标slot |
| title | 标题slot |
| 默认 | 内容slot |
| footer | 右侧区域slot |



================================================
FILE: docs/cells.md
================================================
# Cells
Cells是列表分组,常用于嵌套一组Cell或者[Checkbox](./checkbox.md),注意,Checkbox-group和Cell组件都必须放在Cells组件下面,Cells效果如下图所示。

![](./img/cells.png#width:300px)

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-cells": "weui-miniprogram/cells/cells",
    "mp-cell": "weui-miniprogram/cell/cell"
  }
}
```

## 示例代码
```html
<!--WXML示例代码-->
<mp-cells ext-class="my-cells" title="带说明的列表项">
    <mp-cell value="标题文字" footer="说明文字"></mp-cell>
    <mp-cell>
        <view>标题文字(使用slot)</view>
        <view slot="footer">说明文字</view>
    </mp-cell>
</mp-cells>
```

```js
// page.js示例代码
Page({
});
```


## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| title | string |  | 否 | Cells的标题 |
| footer | string |  | 否 | Cells底部的文字 |

## Slot
| 名称 | 描述 |
| ---- | ---- |
| 默认 | 内容slot |


================================================
FILE: docs/checkbox-group.md
================================================
# Checkbox-group和Checkbox
Checkbox-group是由一组单选或者多选Checkbox组件组成,效果如下图所示。

![](./img/checkbox-group.png#width:300px)

## 引入组件
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-checkbox-group": "weui-miniprogram/checkbox-group/checkbox-group",
    "mp-checkbox": "weui-miniprogram/checkbox/checkbox",
    "mp-cells": "weui-miniprogram/cells/cells"
  }
}
```

## 示例代码
```html
<!--WXML示例代码-->
<mp-cells title="单选列表项">
    <mp-checkbox-group prop="radio" multi="{{false}}" bindchange="radioChange">
        <mp-checkbox wx:for="{{radioItems}}" wx:key="value" label="{{item.name}}" value="{{item.value}}" checked="{{item.checked}}"></mp-checkbox>
    </mp-checkbox-group>
</mp-cells>
<mp-cells title="复选列表项">
    <mp-checkbox-group prop="checkbox" multi="{{true}}" bindchange="checkboxChange">
        <mp-checkbox wx:for="{{checkboxItems}}" wx:key="value" label="{{item.name}}" value="{{item.value}}" checked="{{item.checked}}"></mp-checkbox>
    </mp-checkbox-group>
</mp-cells>
```

```js
// page.js示例代码
Page({
    data: {
        radioItems: [
            {name: 'cell standard', value: '0', checked: true},
            {name: 'cell standard', value: '1'}
        ],
        checkboxItems: [
            {name: 'standard is dealt for u.', value: '0', checked: true},
            {name: 'standard is dealicient for u.', value: '1'}
        ],
    },
    radioChange: function (e) {
        console.log('radio发生change事件,携带value值为:', e.detail.value);

        var radioItems = this.data.radioItems;
        for (var i = 0, len = radioItems.length; i < len; ++i) {
            radioItems[i].checked = radioItems[i].value == e.detail.value;
        }

        this.setData({
            radioItems: radioItems,
            [`formData.radio`]: e.detail.value
        });
    },
    checkboxChange: function (e) {
        console.log('checkbox发生change事件,携带value值为:', e.detail.value);

        var checkboxItems = this.data.checkboxItems, values = e.detail.value;
        for (var i = 0, lenI = checkboxItems.length; i < lenI; ++i) {
            checkboxItems[i].checked = false;

            for (var j = 0, lenJ = values.length; j < lenJ; ++j) {
                if(checkboxItems[i].value == values[j]){
                    checkboxItems[i].checked = true;
                    break;
                }
            }
        }

        this.setData({
            checkboxItems: checkboxItems,
            [`formData.checkbox`]: e.detail.value
        });
    },
});
```

## checkbox-group组件属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| multi | boolean | true | 否 | 单选还是多选 |
| prop | string | | 否 | [Form](./form.md)表单组件校验的字段名 |
| bindchange | eventhandler | | 否 | Checkbox-group发生改变时候触发的事件,detail为{value},单选的value为checkbox的值,多选的value为选中的checkbox的值组成的数组 |

## checkbox-group的Slot
| 名称 | 描述 |
| ---- | ---- |
| 默认 | 内容slot |

## checkbox组件属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| multi | boolean | true | 否 | 单选还是多选 |
| checked | boolean | | 否 | 是否选中 |
| value | string | | 否 | checkbox的值 |
| bindchange | eventhandler | | 否 | Checkbox发生改变时候触发的事件,detail为{value},value为checkbox的值 |


================================================
FILE: docs/dialog.md
================================================
# Dialog
Dialog弹窗组件。

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-dialog": "weui-miniprogram/dialog/dialog"
  }
}
```


## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| title | string |  | 否 | 弹窗的标题 |
| buttons | array\<object\> | [] | 否 | 底部的按钮组,建议最多提供两个按钮 |
| mask | boolean |  | 是 | 是否显示蒙层 |
| mask-closable | boolean |  | 是 | 点击蒙层是否可以关闭 |
| show | boolean | false | 否 | 是否显示弹窗 |
| bindclose | eventhandler |  | 否 | 弹窗关闭的时候触发的事件 |
| bindbuttontap | eventhandler |  | 否 | buttons按钮组点击时触发的事件,detail为{index, item},item是按钮的配置项 |

buttons提供按钮组配置,每一项表示一个按钮,每一项的属性为
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| extClass | string |  | 否 | 按钮的额外的class,可用于修改组件内部的样式 |
| text | string |  | 否 | 按钮的文本 |

## Slot
弹窗组件只有一个默认slot用于显示弹窗的内容


================================================
FILE: docs/form-page.md
================================================
# FormPage
表单页面,规定了标准表单的顶部的标题和底部的按钮提示等区域的规范

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-form-page": "weui-miniprogram/form-page/form-page",
    "mp-form": "weui-miniprogram/form/form"
  }
}
```

## 示例代码
```html
<!--WXML示例代码-->
<mp-form-page title="表单结构" subtitle="展示表单页面的信息结构样式, 分别由头部区域/控件区域/提示区域/操作区域和底部信息区域组成。">
    <mp-form id="form" rules="{{rules}}" models="{{formData}}">
    </mp-form>
    <checkbox-group slot="tips" bindchange="bindAgreeChange">
        <label class="weui-agree" for="weuiAgree">
            <view class="weui-agree__text">
                <checkbox class="weui-agree__checkbox" id="weuiAgree" value="agree" checked="{{isAgree}}" />
                <view class="weui-agree__checkbox-icon">
                    <icon class="weui-agree__checkbox-icon-check" type="success_no_circle" size="9" wx:if="{{isAgree}}"></icon>
                </view>
                阅读并同意<navigator url="" class="weui-agree__link">《相关条款》</navigator>
            </view>
        </label>
    </checkbox-group>
    <view slot="button">
        <button class="weui-btn" type="primary" bindtap="submitForm">确定</button>
    </view>
</mp-form-page>
```

## 效果展示
![](./img/form-page.png#width:300px)

## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| title | string |  | 否 | 标题 |
| subtitle | boolean |  | 否 | 副标题 |

## Slot
| 名称 | 描述 |
| ---- | ---- |
| title | 标题区域slot和title属性互斥 |
| tips | 底部确认按钮前面的提示区域 |
| button | 底部提交按钮区域 |
| suffixtips | 提交按钮下面的提示区域 |
| footer | 页脚的内容区域 |

================================================
FILE: docs/form.md
================================================
# Form
Form表单组件,结合Cell、Checkbox-group、Checkbox组件等做表单校验。

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-form": "weui-miniprogram/form/form"
  }
}
```

## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| rules | object\<array\> |  | 否 | 表单校验的规则列表,格式下面详细介绍 |
| models | object |  | 否 | 需要校验的表单的数据 |
| bindsuccess | eventhandler |  | 否 | 校验成功触发的事件,detail是{trigger},trigger的值是change或validate,表示是输入改成触发的校验还是主动调用的validate接口 |
| bindfail | eventhandler |  | 否 | 校验失败触发的事件,detail是{trigger, errors},trigger的值是change或validate,表示是输入改成触发的校验还是主动调用的validate接口。errors是错误的字段列表。 |

rules是表单校验的规则列表,列表每一项表示一个字段的校验规则,注意,必须要在Cell或Checkbox-group组件声明prop属性,表单校验规则才生效,表单校验规则的定义如下:

| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| name | string |  | 是 | 校验的字段名 |
| rules | array/object |  | 是 | 校验的规则,如果有多项,则是数组 |
| rules.message | string |  | 否 | 校验失败时候提示的文字 |
| rules.validator | function |  | 否 | 自定义校验函数,接受rule, value, param, models四个参数,其中rule格式为{name: '字段名', message: '失败信息'}, value是字段值,param是校验参数,models是form组件的models属性。函数返回错误提示,表示校验失败,错误提示会通过回调返回给开发者 |
| rules.\[rule\] | string |  | 否 | rule是变量,表示内置的校验规则名称,比如required,则校验规则对象为{name: "fieldName", rules: {required: true}},下面会详细介绍所有的内置规则 |

## 内置校验规则
| 规则名 | 参数 | 说明 |
| ---- | ---- | ---- |
| required |  | 是否必填 |
| minlength | number | 最小长度 |
| maxlength | number | 最大长度 |
| rangelength | [number, number] | 长度范围,参数为[最小长度, 最大长度] |
| bytelength | number | 字节长度 |
| range | [number, number] | 数字的大小范围 |
| min | number | 最小值限制 |
| max | number | 最大值限制 |
| mobile |  | 手机号码校验 |
| email |  | 电子邮件校验 |
| url |  | URL链接地址校验 |
| equalTo | string | 相等校验,参数是另外一个字段名 |

## 接口
### validate
validate接口接受类型为function的callback,callback有isValid和errors两个参数,isValid表示是否校验通过,errors在校验失败的时候的值为失败的字段列表。

### validateField
validateField接口接受2个参数,
第一个是字段名,第二个是类型为function的callback,callback有isValid和errors两个参数,isValid表示是否校验通过,errors在校验失败的时候的值为失败的字段列表。

## Slot
| 名称 | 描述 |
| ---- | ---- |
| 默认 | 内容slot |

================================================
FILE: docs/gallery.md
================================================
# Gallery画廊
用于多张图片展示,类似原生的wx.previewImage的展示。

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-gallery": "weui-miniprogram/gallery/gallery"
  }
}
```



## 属性列表
| 属性 | 类型 | 默认值 | 说明 |
| ---- | ---- | ------ | ---- |
| extClass | string |  | 组件类名 |
| show | boolean | true| 组件展示/隐藏 |
| imgUrls | array | [] | 需要展示的图片集 |
| current | number | 0 | 当前展示的图片index |
| showDelete | boolean | true | 是否显删除按钮 |
| hideOnClick | boolean | true | 点击图片是否隐藏该组件 |

## 自定义事件
| 事件名称 | 说明 | 回调参数 |
| ---- | ---- | ------ |
| change | 当前图片滑动切换之后触发 | e.detail = { current } 当前图片的位置 |
| delete | 当前图片被删除后触发(delete 属性需要设为 true) | e.detail = { url, index } 被删除图片的 url 和 index |
| hide | 组件被隐藏时候触发(hideOnClick 属性需要设为 true) | -- |

================================================
FILE: docs/half-screen-dialog.md
================================================
# Half Screen Dialog
半屏弹窗,辅助完成当前页面任务时;提醒用户并引导用户的下一步操作;用户主动发起的任务时。

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-half-screen-dialog": "weui-miniprogram/half-screen-dialog/half-screen-dialog"
  }
}
```

## 示例代码
```html
<!--WXML示例代码-->
<mp-half-screen-dialog 
  bindbuttontap="buttontap"
  show="{{show}}"
  maskClosable="{{false}}" 
  title="测试标题B" 
  subTitle="测试标题B的副标题"
  desc="辅助描述内容,可根据实际需要安排"
  tips="辅助提示内容,可根据实际需要安排"
  buttons="{{buttons}}"
></mp-half-screen-dialog>
<button class="weui-btn" type="primary" bindtap="open">Open</button>
```

```js
// page.js示例代码
Page({
    data: {
        show: false,
        buttons: [
            {
                type: 'default',
                className: '',
                text: '辅助操作',
                value: 0
            },
            {
                type: 'primary',
                className: '',
                text: '主操作',
                value: 1
            }
        ]
    },
    open: function () {
        this.setData({
            show: true
        })
    },
    buttontap(e) {
        console.log(e.detail)
    }
});
```

## 效果展示
![](./img/half-screen-dialog.png#width:300px)

## 属性列表
| 属性 | 类型 | 默认值 | 说明 |
| ---- | ---- | ------ | ---- |
| extClass | string |  | 组件类名 |
| closabled | boolean | true| 是否展示关闭按钮 |
| title | string |  | 组件标题,可通过slot自定义 |
| subTitle | string |  | 组件副标题,可通过slot自定义 |
| desc | string |  | 辅助操作描述内容 |
| tips | string |  | 辅助操作提示内容 |
| maskClosable | boolean | true | 点击遮罩是否关闭该组件 |
| mask | boolean | true | 是否需要遮罩层 |
| show | boolean | true | 是否开启弹窗 |
| buttons | array | [] | 辅助操作按钮列表 |

## 自定义事件
| 事件名称 | 说明 | 回调参数 |
| ---- | ---- | ------ |
| bindbuttontap | 点击辅助操作按钮时触发 | e.detail = { index, item } |
| bindclose | 组件关闭时候触发 |  |

## Slot
| 名称 | 描述 |
| ---- | ---- |
| title | 组件自定义标题栏 |
| desc | 组件自定义操作描述 |
| footer | 操作按钮区域slot |

================================================
FILE: docs/icon.md
================================================
# Icon
图标

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-icon": "weui-miniprogram/icon/icon"
  }
}
```

## 示例代码
```html
<!--WXML示例代码-->
<mp-icon type="field" icon="add" color="black" size="{{25}}"></mp-icon>
<mp-icon icon="add" color="black" size="{{25}}"></mp-icon>
```

## 效果展示
![](./img/icon.png#width:300px)

## 属性列表
| 属性 | 类型 | 默认值 | 说明 |
| ---- | ---- | ------ | ---- |
| extClass | string |  | 组件类名 |
| type | string | outline| Icon类型,可选值 outline(描边),field(填充) |
| icon | string |  | Icon名字 |
| size | number | 20 | Icon的大小,单位 px |
| color | string | black | Icon的颜色,默认黑色 |

## Icon 列表
![](./img/iconList1.jpg#width:1366px)
![](./img/iconList2.jpg#width:1366px)


================================================
FILE: docs/loading.md
================================================
# Loading加载
加载数据时的 loading 效果

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-loading": "weui-miniprogram/loading/loading"
  }
}
```


## 属性列表
| 属性 | 类型 | 默认值 | 说明 |
| ---- | ---- | ------ | ---- |
| extClass | string |  | 组件类名 |
| show | boolean | true | loading 是否展示 |
| animated | boolean | false | loading 显示/消失 动画 |
| duration | number | 350 | 过渡动画时间 |
| type | string | dot-gray | loading 类型,可选值有 dot-white、dot-gray、circle |
| tips | string | 加载中 | 当 type 为 circle时生效,loading辅助文字 |


================================================
FILE: docs/msg.md
================================================
# Msg
Msg组件提供操作确认页或操作成功或失败的标准的确认页的样式。

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-msg": "weui-miniprogram/msg/msg"
  }
}
```



## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| type | string |  | 否 | 顶部图标的样式,和[icon](https://developers.weixin.qq.com/miniprogram/dev/component/icon.html)组件的type属性用法一样 |
| size | number | 64 | 否 | type不为空的时候有效,和[icon](https://developers.weixin.qq.com/miniprogram/dev/component/icon.html)组件的size属性用法一样 |
| icon | string | | 否 | type为空的时候,icon的值是顶部图标的路径 |
| title | string |  | 否 | 标题 |
| desc | string | | 否 | 描述内容,和desc的slot显示在相同的位置 |

## Slot
| 名称 | 描述 |
| ---- | ---- |
| desc | 描述内容slot |
| extend | desc下面的说明区域的slot |
| handle | 操作按钮区域slot |
| footer | 底部slot |


================================================
FILE: docs/navigation.md
================================================
# Navigation
Navigation是小程序的顶部导航组件,当页面配置`navigationStyle`设置为`custom`的时候可以使用此组件替代原生导航栏。

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-navigation-bar": "weui-miniprogram/navigation-bar/navigation-bar"
  }
}
```



## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| title | string |  | 否 | 导航标题,如果不提供,则名为center的slot有效 |
| back | boolean | true | 否 | 是否显示返回按钮,默认点击按钮会执行navigateBack,如果为false,则名为left的slot有效 |
| delta | number | 1 | 否 | 当back为true的时候有效,表示navigateBack的delta参数 |
| background | string | #f8f8f8 | 否 | 导航背景色 |
| color | string |  | 否 | 导航颜色 |
| loading | boolean |  | 否 | 是否显示标题左侧的loading |
| show | boolean |  | 否 | 显示隐藏导航,隐藏的时候navigation的高度占位还在 |
| animated | boolean |  | 否 | 显示隐藏导航的时候是有opacity过渡动画 |
| bindback | eventhandler |  | 否 | 在back为true时,点击back按钮触发此事件,detail包含delta |

## Slot
| 名称 | 描述 |
| ---- | ---- |
| left | 左侧slot,在back按钮位置显示,当back为false的时候有效 |
| center | 标题slot,在标题位置显示,当title为空的时候有效 |
| right | 在导航的右侧显示 |


================================================
FILE: docs/other.md
================================================
# 其他组件

出于性能考虑,`weui-miniprogram` 并没有对所有 WeUI 组件进行封装(如:flex布局组件),可以直接使用 WeUI 中定义的组件结构(参考 [Demo](https://github.com/wechat-miniprogram/weui-miniprogram/tree/master/tools/demo))。

## 示例代码

在引入 weui.wxss 后,可以直接使用 weui-wxss 中定义的 class 自定义样式,以 flex 组件为例:

```html
<view class="page__hd">
    <view class="page__title">Flex</view>
    <view class="page__desc">Flex布局</view>
</view>
<view class="page__bd page__bd_spacing">
    <view class="weui-flex">
        <view class="weui-flex__item"><view class="placeholder">weui</view></view>
    </view>
    <view class="weui-flex">
        <view class="weui-flex__item"><view class="placeholder">weui</view></view>
        <view class="weui-flex__item"><view class="placeholder">weui</view></view>
    </view>
    <view class="weui-flex">
        <view class="weui-flex__item"><view class="placeholder">weui</view></view>
        <view class="weui-flex__item"><view class="placeholder">weui</view></view>
        <view class="weui-flex__item"><view class="placeholder">weui</view></view>
    </view>
    <view class="weui-flex">
        <view class="weui-flex__item"><view class="placeholder">weui</view></view>
        <view class="weui-flex__item"><view class="placeholder">weui</view></view>
        <view class="weui-flex__item"><view class="placeholder">weui</view></view>
        <view class="weui-flex__item"><view class="placeholder">weui</view></view>
    </view>
    <view class="weui-flex">
        <view><view class="placeholder">weui</view></view>
        <view class="weui-flex__item"><view class="placeholder">weui</view></view>
        <view><view class="placeholder">weui</view></view>
    </view>
</view>
```

渲染到页面上会得到以下结果:

![flex布局](./img/flex.png#width:396px)

其他组件可以参考库中提供的 [Demo](https://github.com/wechat-miniprogram/weui-miniprogram/tree/master/tools/demo)

================================================
FILE: docs/quickstart.md
================================================
# 快速上手

## 使用之前

扩展组件库基于小程序自定义组件构建,在使用扩展组件库之前,建议先阅读熟悉小程序[自定义组件](../../framework/custom-component/index.md)。

## 引入组件

1. 通过 [useExtendedLib 扩展库](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#useExtendedLib) 的方式引入,这种方式引入的组件将不会计入代码包大小。
2. 可以通过[npm](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html)方式下载构建,npm包名为`weui-miniprogram`


## 如何使用

首先要在 app.wxss 里面引入 weui.wxss,如果是通过 npm 引入,需要先构建 npm(“工具”菜单 --> “构建 npm”)

**通过 [useExtendedLib 扩展库](https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#useExtendedLib) 的方式引入,可省略 import 步骤**

```css
@import 'weui-miniprogram/weui-wxss/dist/style/weui.wxss';
```

然后可以在页面中引入 dialog 弹窗组件
1. 首先在页面的 json 文件加入 usingComponents 配置字段
```
{
  "usingComponents": {
    "mp-dialog": "weui-miniprogram/dialog/dialog"
  }
}
```

2. 然后可以在对应页面的 wxml 中直接使用该组件

```html
<mp-dialog title="test" show="{{true}}" bindbuttontap="tapDialogButton" buttons="{{buttons}}">
    <view>test content</view>
</mp-dialog>
```

```js
Page({
  data: {
    buttons: [{text: '取消'}, {text: '确认'}]
  }
})
```

完整的组件的使用文档请参考具体的组件的文档。

## 修改组件内部样式
每个组件可以设置`ext-class`这个属性,该属性提供设置在组件WXML顶部元素的class,组件的[addGlobalClass](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/wxml-wxss.html#%E7%BB%84%E4%BB%B6%E6%A0%B7%E5%BC%8F%E9%9A%94%E7%A6%BB)的options都设置为true,所以可以在页面设置wxss样式来覆盖组件的内部样式。需要注意的是,如果要覆盖组件内部样式,必须wxss的样式选择器的优先级比组件内部样式优先级高。
`addGlobalClass`在基础库2.2.3开始支持。

## 适配 DarkMode

在根结点(或组件的外层结点)增加属性 `data-weui-theme="dark"`,即可把 WeUI 组件切换到 DarkMode 的表现,如:

```html
<view data-weui-theme="dark">
    ...
</view>
```

也可以参考库中提供的 [Demo](https://github.com/wechat-miniprogram/weui-miniprogram/tree/master/tools/demo)。

================================================
FILE: docs/search.md
================================================
# Searchbar
搜索组件Searchbar提供搜索的功能,并展示搜索的结果。

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-searchbar": "weui-miniprogram/searchbar/searchbar"
  }
}
```



## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| focus | boolean | false | 否 | 是否在组件开始创建的时候focus搜索输入框 |
| placeholder | string | 搜索 | 否 | 搜索输入框的placeholder |
| value | string |  | 否 | 搜索输入框的默认值 |
| search | function |  | 是 | 输入过程不断调用此函数得到新的搜索结果,参数是输入框的值value,返回Promise实例 |
| throttle | number | 500 | 否 | 调用search函数的间隔,单位ms |
| cancelText | string | 取消 | 否 | 取消按钮的文本 |
| cancel | boolean | true | 否 | 是否显示取消按钮 |
| bindfocus | eventhandle |  | 否 | 在输入框focus的时候触发事件 |
| bindblur | eventhandle |  | 否 | 在输入框blur的时候触发事件 |
| bindclear | eventhandle |  | 否 | 在clear按钮点击的时候触发事件 |
| bindinput | eventhandle |  | 否 | 在输入框输入过程中触发事件 |
| bindselectresult | eventhandle |  | 否 | 在选择搜索结果的时候触发事件 |


================================================
FILE: docs/slideview.md
================================================
# Slideview
左滑删除组件,基础库 2.4.4 开始支持。

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-slideview": "weui-miniprogram/slideview/slideview"
  }
}
```


## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| disable | boolean | false | 否 | 是否禁用slideview |
| buttons | array\<object\> | [] | 是 | 左滑的按钮组,建议最多3个按钮 |
| icon | boolean | false | 否 | 按钮是否是icon |
| show | boolean |  | 否 | 是否显示slideview,可以控制隐藏显示 |
| duration | boolean | 350 | 否 | slideview显示隐藏的动画的时长 |
| throttle | number | 40 | 否 | 手指移动距离超过该值的时候,触发slideview的显示隐藏 |
| bindbuttontap | eventhandler |  | 否 | buttons按钮组点击时触发的事件,detail为{index, data},data是按钮的配置项传入的data参数 |
| bindhide | eventhandler |  | 否 | 隐藏时触发的事件 |
| bindshow | eventhandler |  | 否 | 显示时触发的事件 |

buttons提供按钮组配置,每一项表示一个按钮,每一项的属性为
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| extClass | string |  | 否 | 按钮的额外的class,可用于修改组件内部的样式 |
| type | string | default | 否 | 按钮的类型,取值default和warn,warn是红色按钮 |
| text | string |  | 否 | 按钮的文本 |
| src | string |  | 否 | 如果icon为true,此属性有效 |
| data | any |  | 否 | 触发bindbuttontap回传的数据 |


================================================
FILE: docs/tabbar.md
================================================
# Tabbar
Tabbar组件,也可以用来作为小程序的[自定义Tabbar](https://developers.weixin.qq.com/miniprogram/dev/framework/ability/custom-tabbar.html)使用

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-tabbar": "weui-miniprogram/tabbar/tabbar"
  }
}
```


## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| list | array\<object\> |  | 否 | Tabbar的项的数组,按照规范,至少要有2个Tabbar项 |
| current | number | 0 | 否 | 当前选中的Tabbar项的下标 |
| bindchange | eventhandler |  | 否 | Tabbar项发生改变的时候触发此事件,detail为{index, item},index是Tabbar下标,item是对应的Tabbar项的配置 |

list属性是对象数组,每一项表示一个Tabbar项,其字段含义为
| 字段名 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| text | string |  | 是 | Tabbar项的标题 |
| iconPath | string |  | 否 | Tabbar项的icon图片路径,建议使用绝对路径,相对路径要相对于组件所在目录的。 |
| selectedIconPath | string |  | 否 | Tabbar项选中时的icon,建议使用绝对路径,相对路径要相对于组件所在目录的。 |
| badge | string |  | 否 | 是否显示Tabbar的右上角的Badge |


================================================
FILE: docs/toptips.md
================================================
# Toptips
Toptips顶部错误提示组件,常用于表单校验或提交请求到后台成功或失败的错误提示,如下图所示。

![](./img/toptips.png#width:300px)

## 引入组件
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-toptips": "weui-miniprogram/toptips/toptips"
  }
}
```

## 示例代码
```html
<!--WXML示例代码-->
<mp-toptips msg="{{error}}" type="error" show="{{error}}"></mp-toptips>
```

```js
// page.js示例代码
Page({
    data: {
        error: ''
    },
    onShow() {
        this.setData({
            error: '这是一个错误提示'
        })
    }
});
```

## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| type | string |  | 否 | 提示类型,可以为info、error、success,表示三种提示颜色 |
| show | boolean | false | 否 | 是否显示Toptips |
| msg | string |  | 是 | 提示内容 |
| delay | number | 2000 | 否 | 提示内容显示后隐藏的ms值 |
| bindhide | eventhandler |  | 否 | 顶部提示隐藏的时候触发的事件 |


================================================
FILE: docs/uploader.md
================================================
# Uploader
图片上传Uploader组件。

## 代码引入
在 page.json 中引入组件
```json
{
  "usingComponents": {
    "mp-uploader": "weui-miniprogram/uploader/uploader"
  }
}
```


## 属性列表
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| ext-class | string |  | 否 | 添加在组件内部结构的class,可用于修改组件内部的样式 |
| title | string | 图片上传 | 否 | 组件标题 |
| tips | string | | 否 | 组件的提示 |
| delete | boolean | | 是 | 是否显示删除按钮 |
| size-type | array |  | 是 | 和chooseImage的sizeType参数一样 |
| source-type | array |  | 是 | 和chooseImage的sourceType参数一样 |
| max-size | number | 5 * 1024 * 1024 | 是 | 图片上传的最大文件限制,默认是5M |
| max-count | number | 1 | 否 | 图片上传的个数限制 |
| files | array\<object\> |  | 否 | 当前的图片列表 |
| select | function |  | 否 | 选择图片时的过滤函数,返回true表示图片有效 |
| upload | function |  | 否 | 图片上传的函数,返回Promise,Promise的callback里面必须resolve({urls})表示成功,否则表示失败 |
| bindselect | eventhandler |  | 否 | 图片选择触发的事件,detail为{tempFilePaths, tempFiles, contents},其中tempFiles和tempFilePaths是chooseImage返回的字段,contents表示所选的图片的二进制Buffer列表 |
| bindcancel | eventhandler |  | 否 | 取消图片选择的事件,detail为{} |
| bindsuccess | eventhandler |  | 否 | 图片上传成功的事件,detail为{urls},urls为upload函数上传成功返回的urls参数 |
| bindfail | eventhandler |  | 否 | 图片上传失败的事件,detail为{type, errMsg},type为1表示图片超过大小限制,type为2表示选择图片失败,type为3表示图片上传失败。 |
| binddelete | eventhandler |  | 否 | 删除图片触发的事件,detail为{index, item},index表示删除的图片的下标,item为图片对象。 |

files表示当前的图片列表,每一项的定义为
| 属性 | 类型 | 默认值 | 必填 | 说明 |
| ---- | ---- | ------ | -------- | ---- |
| url | string |  | 是 | 图片链接 |
| loading | boolean |  | 否 | 图片上传中 |
| error | boolean |  | 否 | 图片上传失败 |


================================================
FILE: gulpfile.js
================================================
const gulp = require('gulp')
const clean = require('gulp-clean')

const config = require('./tools/config')
const BuildTask = require('./tools/build')
const id = require('./package.json').name || 'miniprogram-custom-component'

// 构建任务实例
// eslint-disable-next-line no-new
new BuildTask(id, config.entry)

// 清空生成目录和文件
gulp.task('clean', gulp.series(() => gulp.src(config.distPath, {read: false, allowEmpty: true}).pipe(clean()), done => {
  if (config.isDev) {
    return gulp.src(config.demoDist, {read: false, allowEmpty: true})
      .pipe(clean())
  }

  return done()
}))
// 监听文件变化并进行开发模式构建
gulp.task('watch', gulp.series(`${id}-watch`))
// 开发模式构建
gulp.task('dev', gulp.series(`${id}-dev`))
// 生产模式构建
gulp.task('default', gulp.series(`${id}-default`))
// 构建demo
gulp.task('demo', gulp.series(`${id}-demo`))


================================================
FILE: jest.config.js
================================================
module.exports = {
    bail: 1,
    verbose: true,
    testEnvironment: 'jsdom',
    testURL: 'https://jest.test',
    moduleFileExtensions: ['js', 'ts'],
    testMatch: ['<rootDir>/src/**/__test__/**/*.test.{js,ts}'],
    collectCoverageFrom: [
        '<rootDir>/src/**/*.{js,ts}',
        '!<rootDir>/src/weui-wxss/**',
        '!**/__test__/**'
    ],
    snapshotSerializers: ['miniprogram-simulate/jest-snapshot-plugin']
}


================================================
FILE: mpflow.config.js
================================================
const TerserWebpackPlugin = require('terser-webpack-plugin')

module.exports = {
    appId: 'wxe5f52902cf4de896',
    projectName: '小程序自定义组件',
    app: (mode) => (mode !== 'production' ? 'src/app' : undefined),
    pages: (mode) => (mode !== 'production' ? undefined : ['src/components/index']),
    sourceMap: (mode) => mode !== 'production',
    compileType: 'miniprogram',
    outputDir: 'miniprogram_dist',
    plugins: ['@mpflow/plugin-babel', '@mpflow/plugin-typescript', '@mpflow/plugin-css'],
    minimize: false,
    configureWebpackChain: (config) => {
        config.plugin('terser').use(TerserWebpackPlugin, [
            {
                terserOptions: {
                    compress: {
                        directives: false
                    }
                }
            }
        ])
    },
    settings: {
        urlCheck: true,
        es6: false,
        postcss: false,
        preloadBackgroundData: false,
        minified: false,
        newFeature: true,
        coverView: true,
        nodeModules: true,
        autoAudits: false,
        showShadowRootInWxmlPanel: true,
        scopeDataCheck: false,
        checkInvalidKey: true,
        checkSiteMap: true,
        uploadWithSourceMap: true,
        compileHotReLoad: false,
        babelSetting: {
            ignore: [],
            disablePlugins: [],
            outputPath: ''
        },
        useIsolateContext: true,
        useCompilerModule: false,
        userConfirmedUseCompilerModuleSwitch: false,
        compileWorklet: true,
        minifyWXSS: false
    }
}


================================================
FILE: package.json
================================================
{
  "name": "weui-miniprogram",
  "version": "1.5.6",
  "description": "小程序 WeUI 组件库",
  "main": "miniprogram_dist/index.js",
  "files": [
    "miniprogram_dist"
  ],
  "scripts": {
    "submodule": "git submodule update --init --recursive",
    "init": "npm run submodule && npm i",
    "dev": "mpflow-service dev",
    "dev:open": "mpflow-service dev --open",
    "demo": "npm run dev",
    "watch": "npm run dev",
    "build": "mpflow-service build && npm run build-copy-weui && npm run build-fix-wxs",
    "build-copy-weui": "mkdir -p ./miniprogram_dist/weui-wxss/dist/style/ && cp ./miniprogram_dist/index.wxss ./miniprogram_dist/weui-wxss/dist/style/weui.wxss",
    "build-fix-wxs": "sed -i '' 's/\\/_commons/..\\/_commons/g' ./miniprogram_dist/slideview/slideview.wxml",
    "prepublishOnly": "npm run build",
    "dist": "npm run build",
    "clean-dev": "gulp clean --develop",
    "clean": "gulp clean",
    "lint": "eslint \"src/**/*.js\"",
    "lint-tools": "eslint \"tools/**/*.js\" --rule \"import/no-extraneous-dependencies: false\"",
    "test": "jest",
    "test-debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --verbose",
    "codecov": "jest --coverage && codecov"
  },
  "miniprogram": "miniprogram_dist",
  "repository": {
    "type": "git",
    "url": "https://github.com/wechat-miniprogram/weui-miniprogram.git"
  },
  "publishConfig": {
    "registry": "https://registry.npmjs.org"
  },
  "homepage": "https://github.com/wechat-miniprogram/weui-miniprogram",
  "author": "xushengni,tomylin,cunjinli,rockhou",
  "license": "MIT",
  "devDependencies": {
    "@mpflow/plugin-babel": "^0.0.31",
    "@mpflow/plugin-css": "^0.0.27",
    "@mpflow/plugin-typescript": "^0.0.27",
    "@mpflow/service": "^0.0.34",
    "@types/jest": "^25.2.2",
    "@typescript-eslint/eslint-plugin": "^2.23.0",
    "@typescript-eslint/parser": "^2.23.0",
    "autoprefixer": "^6.5.1",
    "codecov": "^3.7.0",
    "colors": "^1.3.1",
    "eslint": "^5.14.1",
    "eslint-config-airbnb-base": "13.1.0",
    "eslint-config-prettier": "^6.10.1",
    "eslint-loader": "^2.1.2",
    "eslint-plugin-import": "^2.16.0",
    "eslint-plugin-node": "^7.0.1",
    "eslint-plugin-prettier": "^3.1.2",
    "eslint-plugin-promise": "^3.8.0",
    "gulp": "^4.0.0",
    "gulp-clean": "^0.4.0",
    "gulp-if": "^2.0.2",
    "gulp-install": "^1.1.0",
    "gulp-less": "^4.0.1",
    "gulp-rename": "^1.4.0",
    "gulp-sourcemaps": "^2.6.5",
    "jest": "^25.5.4",
    "jsdom": "^16.2.2",
    "miniprogram-api-typings": "^2.6.5",
    "miniprogram-simulate": "^1.5.8",
    "prettier": "2.0.1",
    "through2": "^2.0.3",
    "tslint": "^5.13.1",
    "tslint-loader": "^3.5.4",
    "typescript": "^3.3.3333",
    "vinyl": "^2.2.0",
    "weui-wxss": "^2.6.17"
  }
}


================================================
FILE: src/app.js
================================================
const themeListeners = []

App({
    onLaunch: function () {
        console.log('App Launch')
        wx.onThemeChange(({ theme }) => {
            this.themeChanged(theme)
        })
    },
    onShow: function () {
        console.log('App Show')
    },
    onHide: function () {
        console.log('App Hide')
    },
    themeChanged(theme) {
        this.globalData.theme = theme
        themeListeners.forEach((listener) => {
            listener(theme)
        })
    },
    watchThemeChange(listener) {
        if (themeListeners.indexOf(listener) < 0) {
            themeListeners.push(listener)
        }
    },
    unWatchThemeChange(listener) {
        const index = themeListeners.indexOf(listener)
        if (index > -1) {
            themeListeners.splice(index, 1)
        }
    },
    globalData: {
        hasLogin: false,
        theme: wx.getAppBaseInfo().theme,
        GRID_DEMO_URL: '/example/index',
        iconTabbar: require('/example/images/icon_tabbar.png').default
    }
})


================================================
FILE: src/app.json
================================================
{
  "pages": [
    "example/index",

    "example/button/button",
    "example/cell/cell",
    "example/slideview/slideview",
    "example/form/form",
    "example/form-page/form-page",
    "example/slider/slider",
    "example/uploader/uploader",

    "example/article/article",
    "example/badge/badge",
    "example/flex/flex",
    "example/footer/footer",
    "example/gallery/gallery",
    "example/grid/grid",
    "example/icons/icons",
    "example/loadmore/loadmore",
    "example/loading/loading",
    "example/panel/panel",
    "example/preview/preview",
    "example/progress/progress",

    "example/actionsheet/actionsheet",
    "example/dialog/dialog",
    "example/msg/msg",
    "example/msg/msg_text",
    "example/msg/msg_text_primary",
    "example/msg/msg_success",
    "example/msg/msg_fail",
    "example/half-screen-dialog/half-screen-dialog",
    "example/picker/picker",
    "example/toast/toast",
    "example/toptips/toptips",

    "example/navbar/navbar",
    "example/navigation/navigation",
    "example/tabbar/tabbar",

    "example/searchbar/searchbar"
  ],
  "window": {
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "小程序UI组件库",
    "navigationBarBackgroundColor": "#f8f8f8",
    "backgroundColor": "#f8f8f8"
  },
  "networkTimeout": {
    "request": 10000,
    "connectSocket": 10000,
    "uploadFile": 10000,
    "downloadFile": 10000
  },
  "debug": true,
  "style": "v2",
  "darkmode": true,
  "lazyCodeLoading": "requiredComponents",
  "renderer": "skyline",
  "rendererOptions": {
    "skyline": {
      "defaultDisplayBlock": true,
      "defaultContentBox": true
    }
  }
}


================================================
FILE: src/app.less
================================================
@import "./example/common.less";

.page {
  display: flex;
  width: 100vw;
  height: 100vh;
  flex-direction: column;
}

.page-height {
  height: 100%;
  flex: 1 1 0;
  overflow: hidden;
}

navigator text,
text text,
text navigator {
  display: inline;
}

.wx-scroll-view-flex:is(div):not(.wx-scroll-view-flex) {
  width: auto !important;
}


================================================
FILE: src/base/CustomPage.js
================================================
import themeMixin from './behaviors/theme'

const CustomPage = function (options) {
    return Page(
        Object.assign({}, options, {
            behaviors: [themeMixin].concat(options.behaviors || []),
            onLoad(query) {
                const app = getApp()
                this.themeChanged(app.globalData.theme)
                app.watchThemeChange && app.watchThemeChange(this.themeChanged)
                options.onLoad && options.onLoad.call(this, query)
            },
            onUnload() {
                const app = getApp()
                app.unWatchThemeChange && app.unWatchThemeChange(this.themeChanged)
                options.onUnload && options.onUnload.call(this)
            }
        })
    )
}

export default CustomPage


================================================
FILE: src/base/behaviors/theme.js
================================================
module.exports = Behavior({
    data: {
        theme: 'light'
    },
    methods: {
        themeChanged(theme) {
            this.setData({
                theme
            })
        }
    }
})


================================================
FILE: src/components/actionsheet/__test__/__snapshots__/actionsheet.test.ts.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`actionsheet basic actionsheet 1`] = `
<main>
  <mp-actionsheet
    class="comp"
    bind:actiontap="actiontap"
    bind:close="close"
  >
    <wx-root-portal
      enable="{{true}}"
    >
      <wx-view
        class="root-portal-box"
      >
        <wx-view
          ariaLabel="关闭"
          ariaRole="button"
          class="weui-mask weui-animate-fade-in customer-mask-class"
          bind:tap="closeActionSheet"
        />
        <wx-view
          ariaModal="true"
          ariaRole="dialog"
          class="weui-actionsheet "
        >
          <wx-view
            class="weui-actionsheet__title"
            tabindex="0"
          >
            <wx-view
              class="weui-actionsheet__title-text"
            >
              actionsheet title
            </wx-view>
          </wx-view>
          <wx-view
            class="weui-actionsheet__menu"
          >
            <wx-view
              ariaRole="button"
              class="weui-actionsheet__cell "
              data-groupindex="{{0}}"
              data-index="{{0}}"
              data-value="{{1}}"
              hoverClass="weui-active"
              bind:tap="buttonTap"
            >
              
                        item 1
                    
            </wx-view>
            <wx-view
              ariaRole="button"
              class="weui-actionsheet__cell "
              data-groupindex="{{0}}"
              data-index="{{1}}"
              data-value="{{2}}"
              hoverClass="weui-active"
              bind:tap="buttonTap"
            >
              
                        item 2
                    
            </wx-view>
            <wx-view
              ariaRole="button"
              class="weui-actionsheet__cell weui-actionsheet__cell_warn"
              data-groupindex="{{0}}"
              data-index="{{2}}"
              data-value="{{3}}"
              hoverClass="weui-active"
              bind:tap="buttonTap"
            >
              
                        item 3
                    
            </wx-view>
          </wx-view>
          <wx-view
            class="weui-actionsheet__action"
          >
            <wx-view
              ariaRole="button"
              class="weui-actionsheet__cell weui-actionsheet__cell_cancel"
              data-type="close"
              hoverClass="weui-active"
              id="iosActionsheetCancel"
              bind:tap="closeActionSheet"
            >
              actionsheet cancel text
            </wx-view>
          </wx-view>
        </wx-view>
      </wx-view>
    </wx-root-portal>
  </mp-actionsheet>
</main>
`;

exports[`actionsheet no cancel 1`] = `
<main>
  <mp-actionsheet
    class="comp"
    bind:actiontap="actiontap"
    bind:close="close"
  >
    <wx-root-portal
      enable="{{true}}"
    >
      <wx-view
        class="root-portal-box"
      >
        <wx-view
          ariaLabel="关闭"
          ariaRole="button"
          class="weui-mask weui-animate-fade-in customer-mask-class"
          bind:tap="closeActionSheet"
        />
        <wx-view
          ariaModal="true"
          ariaRole="dialog"
          class="weui-actionsheet "
        >
          <wx-view
            class="weui-actionsheet__title"
            tabindex="0"
          >
            <wx-view
              class="weui-actionsheet__title-text"
            >
              actionsheet title
            </wx-view>
          </wx-view>
          <wx-view
            class="weui-actionsheet__action"
          >
            <wx-view
              ariaRole="button"
              class="weui-actionsheet__cell "
              data-groupindex="{{0}}"
              data-index="{{0}}"
              data-value="{{1}}"
              hoverClass="weui-active"
              bind:tap="buttonTap"
            >
              
                        item 1
                    
            </wx-view>
            <wx-view
              ariaRole="button"
              class="weui-actionsheet__cell "
              data-groupindex="{{0}}"
              data-index="{{1}}"
              data-value="{{2}}"
              hoverClass="weui-active"
              bind:tap="buttonTap"
            >
              
                        item 2
                    
            </wx-view>
            <wx-view
              ariaRole="button"
              class="weui-actionsheet__cell weui-actionsheet__cell_warn"
              data-groupindex="{{0}}"
              data-index="{{2}}"
              data-value="{{3}}"
              hoverClass="weui-active"
              bind:tap="buttonTap"
            >
              
                        item 3
                    
            </wx-view>
          </wx-view>
        </wx-view>
      </wx-view>
    </wx-root-portal>
  </mp-actionsheet>
</main>
`;

exports[`actionsheet show on create 1`] = `
<main
  show="{{true}}"
>
  <mp-actionsheet
    class="comp"
    bind:actiontap="actiontap"
    bind:close="close"
  >
    <wx-root-portal
      enable="{{true}}"
    >
      <wx-view
        class="root-portal-box"
      >
        <wx-view
          ariaLabel="关闭"
          ariaRole="button"
          class="weui-mask weui-animate-fade-in customer-mask-class"
          bind:tap="closeActionSheet"
        />
        <wx-view
          ariaModal="true"
          ariaRole="dialog"
          class="weui-actionsheet "
        >
          <wx-view
            class="weui-actionsheet__title"
            tabindex="0"
          >
            <wx-view
              class="weui-actionsheet__title-text"
            >
              actionsheet title
            </wx-view>
          </wx-view>
          <wx-view
            class="weui-actionsheet__menu"
          >
            <wx-view
              ariaRole="button"
              class="weui-actionsheet__cell "
              data-groupindex="{{0}}"
              data-index="{{0}}"
              data-value="{{1}}"
              hoverClass="weui-active"
              bind:tap="buttonTap"
            >
              
                        item 1
                    
            </wx-view>
            <wx-view
              ariaRole="button"
              class="weui-actionsheet__cell "
              data-groupindex="{{0}}"
              data-index="{{1}}"
              data-value="{{2}}"
              hoverClass="weui-active"
              bind:tap="buttonTap"
            >
              
                        item 2
                    
            </wx-view>
            <wx-view
              ariaRole="button"
              class="weui-actionsheet__cell weui-actionsheet__cell_warn"
              data-groupindex="{{0}}"
              data-index="{{2}}"
              data-value="{{3}}"
              hoverClass="weui-active"
              bind:tap="buttonTap"
            >
              
                        item 3
                    
            </wx-view>
          </wx-view>
          <wx-view
            class="weui-actionsheet__action"
          >
            <wx-view
              ariaRole="button"
              class="weui-actionsheet__cell weui-actionsheet__cell_cancel"
              data-type="close"
              hoverClass="weui-active"
              id="iosActionsheetCancel"
              bind:tap="closeActionSheet"
            >
              actionsheet cancel text
            </wx-view>
          </wx-view>
        </wx-view>
      </wx-view>
    </wx-root-portal>
  </mp-actionsheet>
</main>
`;


================================================
FILE: src/components/actionsheet/__test__/actionsheet.test.ts
================================================
import path from 'path'
import simulate from 'miniprogram-simulate'

describe('actionsheet', () => {
    let id: string

    beforeAll(() => {
        id = simulate.load(path.resolve(__dirname, './index'))
    })

    test('basic actionsheet', async () => {
        const container = simulate.render(id)
        container.attach(document.createElement('parent-wrapper'))

        expect(container.toJSON()).toMatchSnapshot()
    })

    test('show on create', async () => {
        const container = simulate.render(id, { show: true })
        container.attach(document.createElement('parent-wrapper'))

        expect(container.toJSON()).toMatchSnapshot()
    })

    test('no cancel', async () => {
        const container = simulate.render(id, { showCancel: false })
        container.attach(document.createElement('parent-wrapper'))

        expect(container.toJSON()).toMatchSnapshot()
    })

    test('touch', async () => {
        const container = simulate.render(id)
        container.attach(document.createElement('parent-wrapper'))
        const comp = container.querySelector('.comp')

        const menu = comp.querySelectorAll('.weui-actionsheet__menu .weui-actionsheet__cell')

        menu[2].dispatchEvent('touchstart')
        menu[2].dispatchEvent('touchend')
        await simulate.sleep(10)
        expect(container.data.tapValue).toBe(3)

        menu[1].dispatchEvent('touchstart')
        menu[1].dispatchEvent('touchend')
        await simulate.sleep(10)
        expect(container.data.tapValue).toBe(2)
    })

    test('close', async () => {
        const container = simulate.render(id)
        container.attach(document.createElement('parent-wrapper'))
        const comp = container.querySelector('.comp')

        const cancel = comp.querySelector('.weui-actionsheet__action .weui-actionsheet__cell')

        cancel.dispatchEvent('touchstart')
        cancel.dispatchEvent('touchend')
        await simulate.sleep(10)
        expect(container.data.show).toBe(false)
        expect(comp.data.show).toBe(false)
    })
})


================================================
FILE: src/components/actionsheet/__test__/index.json
================================================
{
    "component": true,
    "usingComponents": {
        "mp-actionsheet": "../actionsheet"
    }
}

================================================
FILE: src/components/actionsheet/__test__/index.ts
================================================
Component({
    properties: {
        showCancel: {
            type: Boolean,
            value: true
        }
    },
    data: {
        tapValue: 0,
        show: true,
        actions: [
            { text: 'item 1', value: 1 },
            { text: 'item 2', value: 2 },
            { text: 'item 3', type: 'warn', value: 3 }
        ]
    },
    methods: {
        actiontap(event): void {
            this.setData({
                tapValue: event.detail.value
            })
        },
        close(): void {
            this.setData({
                show: false
            })
        }
    }
})


================================================
FILE: src/components/actionsheet/__test__/index.wxml
================================================
<mp-actionsheet
    class="comp"
    title="actionsheet title"
    show-cancel="{{showCancel}}"
    cancel-text="actionsheet cancel text"
    show="{{show}}"
    mask-class="customer-mask-class"
    actions="{{actions}}"
    bindactiontap="actiontap"
    bindclose="close"
>
</mp-actionsheet>


================================================
FILE: src/components/actionsheet/actionsheet.json
================================================
{
  "component": true,
  "styleIsolation": "apply-shared",
  "usingComponents": {}
}

================================================
FILE: src/components/actionsheet/actionsheet.less
================================================
.weui-actionsheet__action {
  padding-bottom: 0;
}

.weui-actionsheet__cell_cancel {
  padding-bottom: calc(16px + env(safe-area-inset-bottom));
}

.weui-actionsheet__title:focus {
  outline: none;
}

.weui-actionsheet {
    transform: none;
    -webkit-transform: none;
}

// FIXME: skyline animation 暂有 scope 的 bug
.weui-animate-fade-in {
    animation: weuiFadeIn ease 0.3s forwards;
}

@keyframes weuiFadeIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

.weui-animate-fade-out {
    animation: weuiFadeOut ease 0.3s forwards;
}

@keyframes weuiFadeOut {
    from {
        opacity: 1;
    }
    to {
        opacity: 0;
    }
}

.weui-animate-slide-up {
    animation: weuiSlideUp ease 0.3s forwards;
}

@keyframes weuiSlideUp {
    from {
        transform: translate3d(0, 100%, 0);
    }
    to {
        transform: translateZ(0);
    }
}

.weui-animate-slide-down {
    animation: weuiSlideDown ease 0.3s forwards;
}

@keyframes weuiSlideDown {
    from {
        transform: translateZ(0);
    }
    to {
        transform: translate3d(0, 100%, 0);
    }
}


================================================
FILE: src/components/actionsheet/actionsheet.ts
================================================
Component({
    options: {
        multipleSlots: true // 在组件定义时的选项中启用多slot支持
    },
    properties: {
        title: {
            // 标题
            type: String,
            value: ''
        },
        showCancel: {
            // 是否显示取消按钮
            type: Boolean,
            value: true
        },
        cancelText: {
            // 取消按钮文案
            type: String,
            value: '取消'
        },
        maskClass: {
            // 遮罩层class
            type: String,
            value: ''
        },
        extClass: {
            // 弹出窗 class
            type: String,
            value: ''
        },
        maskClosable: {
            // 点击遮罩 关闭 actionsheet
            type: Boolean,
            value: true
        },
        mask: {
            // 是否需要 遮罩层
            type: Boolean,
            value: true
        },
        show: {
            // 是否开启 actionsheet
            type: Boolean,
            value: false,
            observer: '_showChange'
        },
        actions: {
            // actions 列表
            type: Array,
            value: [], // {text, extClass}
            observer: '_groupChange'
        },
        rootPortal: {
            // 是否使用 root-portal
            type: Boolean,
            value: false
        }
    },

    data: {
        wrapperShow: false,
        innerShow: false
    },

    lifetimes: {
        ready() {
            this._showChange(this.data.show)
        }
    },

    methods: {
        _showChange(show) {
            if (show) {
                this.setData({
                    wrapperShow: true,
                    innerShow: true
                })
            } else {
                this.setData({
                    innerShow: false
                })
                setTimeout(() => {
                    this.setData({ wrapperShow: false })
                }, 300)
            }
        },
        _groupChange(e): void {
            // 支持 一维数组 写法
            if (e.length > 0 && typeof e[0] !== 'string' && !(e[0] instanceof Array)) {
                this.setData({
                    actions: [this.data.actions]
                })
            }
        },
        buttonTap(e): void {
            const { value, groupindex, index } = e.currentTarget.dataset
            this.triggerEvent('actiontap', { value, groupindex, index })
        },
        closeActionSheet(e): void {
            const { type } = e.currentTarget.dataset
            if (this.data.maskClosable || type) {
                // 点击 action 里面的 取消
                this.setData({
                    show: false
                })
                // 关闭回调事件
                this.triggerEvent('close')
            }
        }
    }
})


================================================
FILE: src/components/actionsheet/actionsheet.wxml
================================================
<wxs module="utils">
    var join = function(a,b) {
        return a+b
    };
    var isNotSlot = function(v) {
        return typeof v !== 'string'
    }
    module.exports = {
        join: join,
        isNotSlot: isNotSlot
    }
</wxs>

<template name="body">
    <view
        wx:if="{{mask}}"
        class="weui-mask {{innerShow ? 'weui-animate-fade-in' : 'weui-animate-fade-out'}} {{maskClass}}"
        bindtap="closeActionSheet"
        aria-role="button"
        aria-label="关闭"
    ></view>
    <view
        class="weui-actionsheet {{innerShow ? 'weui-animate-slide-up' : 'weui-animate-slide-down'}} {{extClass}}"
        aria-role="dialog"
        aria-modal="true"
    >
        <!-- 标题 -->
        <block wx:if="{{title}}">
            <view class="weui-actionsheet__title" tabindex="0">
                <view class="weui-actionsheet__title-text">{{title}}</view>
            </view>
        </block>
        <slot name="title" wx:else></slot>
        <view
            class="{{ !showCancel && index === actions.length-1 ? 'weui-actionsheet__action' : 'weui-actionsheet__menu' }}"
            wx:key="index"
            wx:for-item="actionItem"
            wx:for-index="index"
            wx:for="{{actions}}"
        >
            <block wx:if="{{utils.isNotSlot(actionItem)}}">
                <view
                    class="weui-actionsheet__cell {{item.type === 'warn' ? 'weui-actionsheet__cell_warn' : '' }}"
                    aria-role="button"
                    hover-class="weui-active"
                    wx:key="actionIndex"
                    wx:for="{{actionItem}}"
                    wx:for-index="actionIndex"
                    data-groupindex="{{index}}"
                    data-index="{{actionIndex}}"
                    data-value="{{item.value}}"
                    bindtap="buttonTap"
                >
                    {{item.text}}
                </view>
            </block>
            <slot name="{{actionItem}}" wx:else></slot>
        </view>
        <!-- 取消按钮 -->
        <view class="weui-actionsheet__action" wx:if="{{showCancel}}">
            <view
                class="weui-actionsheet__cell weui-actionsheet__cell_cancel"
                hover-class="weui-active"
                data-type="close"
                id="iosActionsheetCancel"
                bindtap="closeActionSheet"
                aria-role="button"
            >{{cancelText}}</view>
        </view>
    </view>
</template>

<root-portal enable="{{true}}" wx:if="{{rootPortal && wrapperShow}}">
    <view class="root-portal-box">
        <template is="body" data="{{mask, innerShow, maskClass, extClass, title, showCancel, cancelText, maskClosable, actions}}" />
    </view>
</root-portal>
<block wx:elif="{{!rootPortal && wrapperShow}}">
    <template is="body" data="{{mask, innerShow, maskClass, extClass, title, showCancel, cancelText, maskClosable, actions}}" />
</block>


================================================
FILE: src/components/badge/__test__/__snapshots__/badage.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`badge badge with content 1`] = `
<main>
  <wx-view
    ariaLabel=""
    class="weui-badge  "
  >
    badge content
  </wx-view>
</main>
`;

exports[`badge badge without content 1`] = `
<main>
  <wx-view
    ariaLabel=""
    class="weui-badge  weui-badge_dot"
  >
    
  </wx-view>
</main>
`;


================================================
FILE: src/components/badge/__test__/badage.test.js
================================================
import path from 'path'
import simulate from 'miniprogram-simulate'

describe('badge', () => {
    let id

    beforeAll(() => {
        id = simulate.load(path.resolve(__dirname, '../badge'), { less: true })
    })

    test('badge with content', () => {
        const content = 'badge content'
        const comp = simulate.render(id, {
            content: content
        })
        comp.attach(document.createElement('parent-wrapper'))

        expect(comp.toJSON()).toMatchSnapshot()
    })

    test('badge without content', () => {
        const comp = simulate.render(id, {})
        comp.attach(document.createElement('parent-wrapper'))

        expect(comp.toJSON()).toMatchSnapshot()
    })
})


================================================
FILE: src/components/badge/badge.json
================================================
{
    "component": true,
    "styleIsolation": "apply-shared",
    "usingComponents": {}
}

================================================
FILE: src/components/badge/badge.less
================================================


================================================
FILE: src/components/badge/badge.ts
================================================
Component({
    properties: {
        extClass: {
            type: String,
            value: ''
        },
        content: {
            type: String,
            value: ''
        },
        ariaLabel: {
            type: String,
            value: ''
        }
    }
})


================================================
FILE: src/components/badge/badge.wxml
================================================
<view class="weui-badge {{extClass}} {{!content ? 'weui-badge_dot' : ''}}" aria-label="{{ariaLabel}}">{{content}}</view>

================================================
FILE: src/components/cell/cell.json
================================================
{
    "component": true,
    "styleIsolation": "apply-shared",
    "usingComponents": {
        "mp-cells": "../cells/cells"
    }
}

================================================
FILE: src/components/cell/cell.less
================================================
.weui-cell_wxss.weui-cell_wxss::before{
  display: flex;
}

/* skyline 不支持 currentColor */
.weui-cell_access .weui-cell__ft::after {
    background-color: var(--weui-FG-2);
}


================================================
FILE: src/components/cell/cell.ts
================================================
Component({
    options: {
        multipleSlots: true
    },
    properties: {
        hover: {
            type: Boolean,
            value: false
        },
        link: {
            type: Boolean,
            value: false
        },
        extClass: {
            type: String,
            value: ''
        },
        iconClass: {
            type: String,
            value: ''
        },
        bodyClass: {
            type: String,
            value: ''
        },
        icon: {
            type: String,
            value: ''
        },
        title: {
            // 和icon二选一,都是放在cell_hd里面
            type: String,
            value: ''
        },
        value: {
            type: String,
            value: ''
        },
        showError: {
            type: Boolean,
            value: false
        },
        prop: {
            // 校验的属性,给父元素form使用
            type: String,
            value: ''
        },
        url: {
            // 在link为true的时候有效,表示navigator的跳转url
            type: String,
            value: ''
        },
        footerClass: {
            type: String,
            value: ''
        },
        footer: {
            type: String,
            value: ''
        },
        inline: {
            // 左右布局样式还是上下布局
            type: Boolean,
            value: true
        },
        hasHeader: {
            type: Boolean,
            value: true
        },
        hasFooter: {
            type: Boolean,
            value: true
        },
        hasBody: {
            type: Boolean,
            value: true
        },
        extHoverClass: {
            // 提供给需要定制 hover-class 的场景,要求 hover 为 false
            type: String,
            value: ''
        },
        ariaRole: {
            type: String,
            value: ''
        }
    },
    relations: {
        '../form/form': {
            type: 'ancestor'
        },
        '../cells/cells': {
            type: 'ancestor'
        }
    },
    data: {
        inForm: false
    },
    methods: {
        setError(error) {
            this.setData({
                error: error || false
            })
        },
        setInForm() {
            this.setData({
                inForm: true
            })
        },
        setOuterClass(className) {
            this.setData({
                outerClass: className
            })
        },
        navigateTo() {
            const data: any = this.data
            if (data.url && data.link) {
                wx.navigateTo({
                    url: data.url,
                    success: (res) => {
                        this.triggerEvent('navigatesuccess', res, {})
                    },
                    fail: (fail) => {
                        this.triggerEvent('navigateerror', fail, {})
                    }
                })
            }
        }
    }
})


================================================
FILE: src/components/cell/cell.wxml
================================================
<view
    bindtap="navigateTo"
    class="weui-cell {{link ? 'weui-cell_access' : ''}} {{extClass}} {{outerClass}} {{inForm ? ' weui-cell-inform' : ''}}{{inline ? '' : 'weui-cell_label-block'}}"
    hover-class="{{hover ? 'weui-hover-active' : extHoverClass}}"
    aria-role="{{ariaRole}}"
>
    <view wx:if="{{hasHeader}}" class="weui-cell__hd {{iconClass}}">
        <block wx:if="{{icon}}">
            <image src="{{icon}}" class="weui-cell__icon" mode="aspectFit"></image>
        </block>
        <block wx:else>
            <slot name="icon"></slot>
        </block>
        <block wx:if="{{inForm}}">
            <block wx:if="{{title}}"><view class="weui-label">{{title}}</view></block>
            <block wx:else>
                <slot name="title"></slot>
            </block>
        </block>
        <block wx:else>
            <block wx:if="{{title}}">{{title}}</block>
            <block wx:else>
                <slot name="title"></slot>
            </block>
        </block>
    </view>
    <view wx:if="{{hasBody}}" class="weui-cell__bd">
        <block wx:if="{{value}}">{{value}}</block>
        <block wx:else>
            <slot></slot>
        </block>
    </view>
    <view wx:if="{{hasFooter}}" class="weui-cell__ft weui-cell__ft_in-access {{footerClass}}">
        <block wx:if="{{footer}}">{{footer}}</block>
        <block wx:else>
            <slot name="footer"></slot>
        </block>
        <icon wx:if="{{showError && error}}" type="warn" size="23" color="#E64340"></icon>
    </view>
</view>


================================================
FILE: src/components/cells/__test__/__snapshots__/cells.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`cells & cell basic cells & cell 1`] = `
<main>
  <mp-cells
    class="cells"
  >
    <wx-view
      ariaRole=""
      class=" weui-cells__group  "
    >
      <wx-view
        class="weui-cells__title"
      >
        带图标、说明的列表项
      </wx-view>
      <wx-view
        class="weui-cells weui-cells_after-title "
      >
        <mp-cell
          class="cell1"
        >
          <wx-view
            ariaRole=""
            class="weui-cell    "
            hoverClass=""
            bind:tap="navigateTo"
          >
            <wx-view
              class="weui-cell__hd "
            >
              <wx-image
                class="weui-cell__icon"
                mode="aspectFit"
                src="success"
              />
            </wx-view>
            <wx-view
              class="weui-cell__bd"
            >
              标题文字
            </wx-view>
            <wx-view
              class="weui-cell__ft weui-cell__ft_in-access "
            >
              说明文字
            </wx-view>
          </wx-view>
        </mp-cell>
        <mp-cell
          class="cell2"
        >
          <wx-view
            ariaRole=""
            class="weui-cell   weui-cell_wxss "
            hoverClass=""
            bind:tap="navigateTo"
          >
            <wx-view
              class="weui-cell__hd "
            />
            <wx-view
              class="weui-cell__bd"
            >
              标题文字
            </wx-view>
            <wx-view
              class="weui-cell__ft weui-cell__ft_in-access "
            >
              说明文字
            </wx-view>
          </wx-view>
        </mp-cell>
      </wx-view>
      <wx-view
        class="weui-cells__tips"
      >
        底部说明文字
      </wx-view>
    </wx-view>
  </mp-cells>
</main>
`;


================================================
FILE: src/components/cells/__test__/cells.test.js
================================================
import path from 'path'
import simulate from 'miniprogram-simulate'

describe('cells & cell', () => {
    let id

    beforeAll(() => {
        id = simulate.load(path.resolve(__dirname, './index'), { less: true })
    })

    test('basic cells & cell', async () => {
        const container = simulate.render(id)
        container.attach(document.createElement('parent-wrapper'))
        expect(container.toJSON()).toMatchSnapshot()
    })
})


================================================
FILE: src/components/cells/__test__/index.js
================================================
Component({
    data: {
        tapValue: 0,
        actions: [
            { text: 'item 1', value: 1 },
            { text: 'item 2', value: 2 },
            { text: 'item 3', type: 'warn', value: 3 }
        ]
    },
    methods: {
        actiontap(event) {
            this.setData({
                tapValue: event.detail.value
            })
        },
        close() {
            this.setData({
                close: 'true'
            })
        }
    }
})


================================================
FILE: src/components/cells/__test__/index.json
================================================
{
    "component": true,
    "usingComponents": {
        "mp-cells": "../../cells/cells",
        "mp-cell": "../../cell/cell"
    }
}

================================================
FILE: src/components/cells/__test__/index.wxml
================================================
<mp-cells class="cells" title="带图标、说明的列表项" footer="底部说明文字">
    <mp-cell class="cell1" value="标题文字" footer="说明文字" icon="success"></mp-cell>
    <mp-cell class="cell2" value="标题文字" footer="说明文字"></mp-cell>
</mp-cells>

================================================
FILE: src/components/cells/cells.json
================================================
{
    "component": true,
    "styleIsolation": "apply-shared",
    "usingComponents": {}
}

================================================
FILE: src/components/cells/cells.less
================================================
.weui-cells__group_wxss.weui-cells__group_wxss .weui-cells__title {
  margin-top: 24px;
}
.weui-cells__group_form .weui-cells__tips {
  margin-top: 8px;
  padding: 0 32px;
  color: var(--weui-FG-1);
}


================================================
FILE: src/components/cells/cells.ts
================================================
// import checkboxGroup from "../checkbox-group/checkbox-group"

Component({
    options: {
        multipleSlots: true
    },
    properties: {
        title: {
            type: String,
            value: ''
        },
        extClass: {
            type: String,
            value: ''
        },
        footer: {
            type: String,
            value: ''
        },
        ariaRole: {
            type: String,
            value: ''
        }
    },
    data: {
        firstItem: null,
        checkboxCount: 0,
        checkboxIsMulti: false,
        outerClass: '',
        childClass: ''
    },
    relations: {
        '../cell/cell': {
            type: 'descendant',
            linked(target) {
                if (!this.data.firstItem) {
                    this.data.firstItem = target
                }
                if (target !== this.data.firstItem) {
                    target.setOuterClass('weui-cell_wxss')
                }
            }
        },
        '../form-page/form-page': {
            type: 'ancestor'
        },
        '../checkbox-group/checkbox-group': {
            type: 'descendant',
            // target: checkboxGroup,
            linked(target) {
                this.setData({
                    checkboxCount: this.data.checkboxCount + 1,
                    checkboxIsMulti: target.data.multi
                })
            },
            unlinked(target) {
                this.setData({
                    checkboxCount: this.data.checkboxCount - 1,
                    checkboxIsMulti: target.data.multi
                })
            }
        }
    },
    methods: {
        setCellMulti(multi) {
            this.setData({
                checkboxIsMulti: multi
            })
        },
        setCellsClass(className) {
            this.setData({
                childClass: className
            })
        },
        setOuterClass(className) {
            this.setData({
                outerClass: className
            })
        }
    }
})


================================================
FILE: src/components/cells/cells.wxml
================================================
<view class="{{extClass}} weui-cells__group {{outerClass}} {{childClass}}" aria-role="{{ariaRole}}">
    <view wx:if="{{title}}" class="weui-cells__title">{{title}}</view>
    <view class="weui-cells weui-cells_after-title {{checkboxCount > 0 && checkboxIsMulti ? 'weui-cells_checkbox' : ''}}">
        <slot></slot>
    </view>
    <view wx:if="{{footer}}" class="weui-cells__tips">{{footer}}</view>
    <slot name="footer" wx:else></slot>
</view>

================================================
FILE: src/components/checkbox/__test__/__snapshots__/checkbox.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`checkbox-group & checkbox basic checkbox 1`] = `
<main>
  <mp-cells
    title="单选列表项"
  >
    <mp-checkbox-group
      class="radio-group"
      bind:change="radioChange"
    >
      <wx-radio-group
        class=""
        bind:change="checkedChange"
      >
        <mp-checkbox
          class="test-radio"
        >
          <wx-label
            bind:tap="checkedChange"
          >
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell  weui-check__label   ^weui-cell_radio  "
                hoverClass="weui-active"
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-view>
                    cell standard
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                >
                  <wx-view
                    slot="footer"
                  >
                    <wx-radio
                      checked="{{false}}"
                      class="weui-check"
                      color=""
                      disabled="{{false}}"
                      value="0"
                    />
                    <wx-view
                      class="weui-icon-checked checkbox--weui-icon-checked "
                    />
                  </wx-view>
                </wx-view>
              </wx-view>
            </mp-cell>
          </wx-label>
        </mp-checkbox>
        <mp-checkbox
          class="test-radio"
        >
          <wx-label
            bind:tap="checkedChange"
          >
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell  weui-check__label weui-cell_wxss  ^weui-cell_radio  "
                hoverClass="weui-active"
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-view>
                    cell standard
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                >
                  <wx-view
                    slot="footer"
                  >
                    <wx-radio
                      checked="{{false}}"
                      class="weui-check"
                      color=""
                      disabled="{{false}}"
                      value="1"
                    />
                    <wx-view
                      class="weui-icon-checked checkbox--weui-icon-checked "
                    />
                  </wx-view>
                </wx-view>
              </wx-view>
            </mp-cell>
          </wx-label>
        </mp-checkbox>
      </wx-radio-group>
    </mp-checkbox-group>
  </mp-cells>
  <mp-cells
    title="复选列表项"
  >
    <mp-checkbox-group
      class="checkbox-group"
      bind:change="checkboxChange"
    >
      <wx-checkbox-group
        class=""
        bind:change="checkedChange"
      >
        <mp-checkbox
          class="test-checkbox"
        >
          <wx-label
            bind:tap="checkedChange"
          >
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell  weui-check__label   ^weui-cell_checkbox  "
                hoverClass="weui-active"
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    slot="icon"
                  >
                    <wx-checkbox
                      checked="{{false}}"
                      class="weui-check"
                      color=""
                      disabled="{{false}}"
                      value="0"
                    />
                    <wx-view
                      class="weui-icon-checked checkbox--weui-icon-checked "
                    />
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-view>
                    standard is dealt for u.
                  </wx-view>
                </wx-view>
              </wx-view>
            </mp-cell>
          </wx-label>
        </mp-checkbox>
        <mp-checkbox
          class="test-checkbox"
        >
          <wx-label
            bind:tap="checkedChange"
          >
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell  weui-check__label weui-cell_wxss  ^weui-cell_checkbox  "
                hoverClass="weui-active"
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    slot="icon"
                  >
                    <wx-checkbox
                      checked="{{false}}"
                      class="weui-check"
                      color=""
                      disabled="{{false}}"
                      value="1"
                    />
                    <wx-view
                      class="weui-icon-checked checkbox--weui-icon-checked "
                    />
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-view>
                    standard is dealicient for u.
                  </wx-view>
                </wx-view>
              </wx-view>
            </mp-cell>
          </wx-label>
        </mp-checkbox>
      </wx-checkbox-group>
    </mp-checkbox-group>
  </mp-cells>
</main>
`;


================================================
FILE: src/components/checkbox/__test__/checkbox.test.js
================================================
import path from 'path'
import simulate from 'miniprogram-simulate'

describe('checkbox-group & checkbox', () => {
    let id

    beforeAll(() => {
        id = simulate.load(path.resolve(__dirname, './index'), { less: true })
    })

    test('basic checkbox', async () => {
        const container = simulate.render(id)
        container.attach(document.createElement('parent-wrapper'))
        expect(container.toJSON()).toMatchSnapshot()
    })

    test('radio value', async () => {
        const container = simulate.render(id)
        container.attach(document.createElement('parent-wrapper'))

        const radioGroup = container.querySelector('.radio-group')

        radioGroup.dispatchEvent('change', {
            detail: {
                value: '0'
            }
        })
        await simulate.sleep(10)
        expect(container.data.radioValue).toBe('0')

        radioGroup.dispatchEvent('change', {
            detail: {
                value: '1'
            }
        })
        await simulate.sleep(10)
        expect(container.data.radioValue).toBe('1')
    })

    test('checkbox value', async () => {
        const container = simulate.render(id)
        container.attach(document.createElement('parent-wrapper'))

        const checkboxGroup = container.querySelector('.checkbox-group')

        checkboxGroup.dispatchEvent('change', {
            detail: {
                value: ['0', '1']
            }
        })
        await simulate.sleep(10)
        expect(container.data.checkboxValue).toBe('0,1')

        checkboxGroup.dispatchEvent('change', {
            detail: {
                value: ['1']
            }
        })
        await simulate.sleep(10)
        expect(container.data.checkboxValue).toBe('1')
    })
})


================================================
FILE: src/components/checkbox/__test__/index.js
================================================
Component({
    data: {
        radioItems: [
            { name: 'cell standard', value: '0' },
            { name: 'cell standard', value: '1' }
        ],
        checkboxItems: [
            { name: 'standard is dealt for u.', value: '0' },
            { name: 'standard is dealicient for u.', value: '1' }
        ],
        radioValue: '',
        checkboxValue: 0
    },
    methods: {
        radioChange(e) {
            this.setData({
                radioValue: e.detail.value
            })
        },
        checkboxChange(e) {
            this.setData({
                checkboxValue: e.detail.value.join(',')
            })
        }
    }
})


================================================
FILE: src/components/checkbox/__test__/index.json
================================================
{
    "component": true,
    "usingComponents": {
        "mp-checkbox-group": "../../checkbox-group/checkbox-group",
        "mp-checkbox": "../../checkbox/checkbox"
    }
}

================================================
FILE: src/components/checkbox/__test__/index.wxml
================================================
<mp-cells title="单选列表项">
    <mp-checkbox-group class="radio-group" prop="radio" multi="{{false}}" bindchange="radioChange">
        <mp-checkbox class="test-radio" wx:for="{{radioItems}}" wx:key="value" label="{{item.name}}" value="{{item.value}}" checked="{{item.checked}}"></mp-checkbox>
    </mp-checkbox-group>
</mp-cells>
<mp-cells title="复选列表项">
    <mp-checkbox-group class="checkbox-group" prop="checkbox" multi="{{true}}" bindchange="checkboxChange">
        <mp-checkbox class="test-checkbox" wx:for="{{checkboxItems}}" wx:key="value" label="{{item.name}}" value="{{item.value}}" checked="{{item.checked}}"></mp-checkbox>
    </mp-checkbox-group>
</mp-cells>

================================================
FILE: src/components/checkbox/checkbox.json
================================================
{
    "component": true,
    "styleIsolation": "apply-shared",
    "usingComponents": {
        "mp-cell": "../cell/cell",
        "mp-checkbox-group": "../checkbox-group/checkbox-group"
    }
}


================================================
FILE: src/components/checkbox/checkbox.less
================================================
@import (reference) '~weui-wxss/src/style/widget/weui-cell/weui-check/weui-radio.less';
@import (reference) '~weui-wxss/src/style/widget/weui-cell/weui-check/weui-checkbox.less';

.weui-cell_wxss.weui-cell_wxss::before{
  display: flex;
}

.weui-cell_radio .weui-check+.weui-icon-checked {
  color: transparent;
}

.weui-cell_radio .weui-check + .weui-icon-checked {
  &.weui-icon-is-checked {
    /* skyline 不支持 attribute selector */
    &:extend(.weui-cells_radio .weui-check:checked + .weui-icon-checked);
    background-color: @weuiColorPrimary;
  }
  background-color: transparent;
}

.weui-cell_checkbox .weui-check + .weui-icon-checked {
  &.weui-icon-is-checked {
    /* skyline 不支持 attribute selector */
    &:extend(.weui-cells_checkbox .weui-check:checked + .weui-icon-checked);
  }
  &:extend(.weui-cells_checkbox .weui-icon-checked);
}

================================================
FILE: src/components/checkbox/checkbox.ts
================================================
Component({
    options: {
        multipleSlots: true
    },
    properties: {
        multi: {
            type: Boolean,
            value: true
        },
        checked: {
            type: Boolean,
            value: false
        },
        value: {
            type: String,
            value: ''
        },
        label: {
            type: String,
            value: 'label'
        },
        extClass: {
            type: String,
            value: ''
        },
        disabled: {
            type: Boolean,
            value: false
        }
    },
    data: {},
    relations: {
        '../checkbox-group/checkbox-group': {
            type: 'ancestor',
            linked(target) {
                this.data.group = target
            },
            unlinked() {
                this.data.group = null
            }
        }
    },
    methods: {
        setMulti(multi) {
            this.setData({
                multi
            })
        },
        setOuterClass(className) {
            this.setData({
                outerClass: className
            })
        },
        checkedChange() {
            if (this.data.disabled) return
            if (this.data.multi) {
                const checked = !this.data.checked
                this.setData({
                    checked
                })
                if (this.data.group) {
                    this.data.group.checkedChange(checked, this)
                }
            } else {
                const checked = this.data.checked
                if (checked) return
                this.setData({
                    checked: true
                })
                if (this.data.group) {
                    this.data.group.checkedChange(checked, this)
                }
            }
            this.triggerEvent('change', { value: this.data.value, checked: this.data.checked })
        }
    }
})


================================================
FILE: src/components/checkbox/checkbox.wxml
================================================
<label bindtap="checkedChange">
  <mp-cell
    has-footer="{{!multi}}"
    has-header="{{multi}}"
    ext-class="weui-check__label {{outerClass}} {{extClass}} {{!multi ? 'weui-cell_radio' : 'weui-cell_checkbox'}}"
    ext-hover-class="weui-active">

    <view slot="icon" wx:if="{{multi}}">
      <checkbox value="{{value}}" checked="{{checked}}" disabled="{{disabled}}" color="{{color}}" class="weui-check">
      </checkbox>
      <view class="weui-icon-checked checkbox--weui-icon-checked {{checked ? 'weui-icon-is-checked' : ''}}"></view>
    </view>
    <view>{{label}}</view>
    <view slot="footer" wx:if="{{!multi}}">
      <radio value="{{value}}" checked="{{checked}}" disabled="{{disabled}}" color="{{color}}" class="weui-check"></radio>
      <view class="weui-icon-checked checkbox--weui-icon-checked {{checked ? 'weui-icon-is-checked' : ''}}"></view>
    </view>
  </mp-cell>
</label>

================================================
FILE: src/components/checkbox-group/checkbox-group.json
================================================
{
    "component": true,
    "styleIsolation": "apply-shared",
    "usingComponents": {
        "mp-cells": "../cells/cells"
    }
}

================================================
FILE: src/components/checkbox-group/checkbox-group.less
================================================


================================================
FILE: src/components/checkbox-group/checkbox-group.ts
================================================
// import cells from "../cells/cells"

Component({
    options: {
        multipleSlots: true
    },
    properties: {
        multi: {
            type: Boolean,
            value: true,
            observer: '_multiChange'
        },
        extClass: {
            type: String,
            value: ''
        },
        prop: {
            type: String,
            value: ''
        }
    },
    data: {
        targetList: [],
        parentCell: null
    },
    relations: {
        '../checkbox/checkbox': {
            type: 'descendant',
            linked(target) {
                this.data.targetList.push(target)
                target.setMulti(this.data.multi)

                if (!this.data.firstItem) {
                    this.data.firstItem = target
                }
                if (target !== this.data.firstItem) {
                    target.setOuterClass('weui-cell_wxss')
                }
            },
            unlinked(target) {
                let index = -1
                this.data.targetList.forEach((item, idx) => {
                    if (item === target) {
                        index = idx
                    }
                })
                this.data.targetList.splice(index, 1)
                if (!this.data.targetList) {
                    this.data.firstItem = null
                }
            }
        },
        '../form/form': {
            type: 'ancestor'
        },
        '../cells/cells': {
            type: 'ancestor',
            // target: cells,
            linked(target) {
                if (!this.data.parentCell) {
                    this.data.parentCell = target
                }
                this.setParentCellsClass()
            },
            unlinked() {
                this.data.parentCell = null // 方便内存回收
            }
        }
    },
    methods: {
        checkedChange(checked, target) {
            if (this.data.multi) {
                const vals = []
                this.data.targetList.forEach((item) => {
                    if (item.data.disabled) return
                    if (item.data.checked) {
                        vals.push(item.data.value)
                    }
                })
                this.triggerEvent('change', { value: vals })
            } else {
                let val = ''
                this.data.targetList.forEach((item) => {
                    if (item.data.disabled) return
                    if (item === target) {
                        val = item.data.value
                    } else {
                        item.setData({
                            checked: false
                        })
                    }
                })
                this.triggerEvent('change', { value: val }, {})
            }
        },
        setParentCellsClass() {
            const className = this.data.multi ? 'weui-cells_checkbox' : 'weui-cells_radio'
            if (this.data.parentCell) {
                this.data.parentCell.setCellsClass(className)
            }
        },
        _multiChange(multi) {
            this.data.targetList.forEach((target) => {
                target.setMulti(multi)
            })
            if (this.data.parentCell) {
                this.data.parentCell.setCellMulti(multi)
            }
            return multi
        }
    }
})


================================================
FILE: src/components/checkbox-group/checkbox-group.wxml
================================================
<checkbox-group wx:if="{{multi}}" class="{{extClass}}" bindchange="checkedChange">
    <slot></slot>
</checkbox-group>
<radio-group wx:else class="{{extClass}}" bindchange="checkedChange">
    <slot></slot>
</radio-group>

================================================
FILE: src/components/dialog/__test__/__snapshots__/dialog.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`dialog basic dialog 1`] = `
<main>
  <wx-root-portal
    enable="{{true}}"
  >
    <wx-view
      ariaModal="true"
      ariaRole="dialog"
      class="root-portal-box weui-dialog__root weui-animate-fade-in"
      style="z-index:901"
    >
      <wx-view
        class="weui-dialog"
        catch:tap="stopEvent"
      >
        <wx-view
          class="weui-dialog__hd"
        >
          <wx-view
            class="weui-dialog__title"
            tabindex="0"
          >
            dialog title
            
          </wx-view>
        </wx-view>
        <wx-view
          class="weui-dialog__bd"
        />
        <wx-view
          class="weui-dialog__ft"
        >
          <wx-view
            ariaRole="button"
            class="weui-dialog__btn weui-dialog__btn_default "
            data-index="{{0}}"
            hoverClass="weui-active"
            bind:tap="buttonTap"
          >
            取消
          </wx-view>
          <wx-view
            ariaRole="button"
            class="weui-dialog__btn weui-dialog__btn_primary "
            data-index="{{1}}"
            hoverClass="weui-active"
            bind:tap="buttonTap"
          >
            确定
          </wx-view>
        </wx-view>
      </wx-view>
    </wx-view>
    <wx-view
      ariaLabel="关闭"
      ariaRole="button"
      class="weui-mask"
      style="z-index:900"
      bind:tap="close"
    />
  </wx-root-portal>
</main>
`;

exports[`dialog close 1`] = `
<main>
  <wx-root-portal
    enable="{{true}}"
  >
    <wx-view
      ariaModal="true"
      ariaRole="dialog"
      class="root-portal-box weui-dialog__root weui-animate-fade-out"
      style="z-index:901"
    >
      <wx-view
        class="weui-dialog"
        catch:tap="stopEvent"
      >
        <wx-view
          class="weui-dialog__hd"
        >
          <wx-view
            class="weui-dialog__title"
            tabindex="0"
          >
            dialog title
            
          </wx-view>
        </wx-view>
        <wx-view
          class="weui-dialog__bd"
        />
        <wx-view
          class="weui-dialog__ft"
        >
          <wx-view
            ariaRole="button"
            class="weui-dialog__btn  "
            data-index="{{0}}"
            hoverClass="weui-active"
            bind:tap="buttonTap"
          >
            取消
          </wx-view>
          <wx-view
            ariaRole="button"
            class="weui-dialog__btn  "
            data-index="{{1}}"
            hoverClass="weui-active"
            bind:tap="buttonTap"
          >
            确定
          </wx-view>
        </wx-view>
      </wx-view>
    </wx-view>
    <wx-view
      ariaLabel="关闭"
      ariaRole="button"
      class="weui-mask"
      style="z-index:900"
      bind:tap="close"
    />
  </wx-root-portal>
</main>
`;


================================================
FILE: src/components/dialog/__test__/dialog.test.js
================================================
import path from 'path'
import simulate from 'miniprogram-simulate'

describe('dialog', () => {
    let id

    beforeAll(() => {
        id = simulate.load(path.resolve(__dirname, './index'), { less: true })
    })

    test('basic dialog', async () => {
        const container = simulate.render(id)
        container.attach(document.createElement('parent-wrapper'))
        expect(container.toJSON()).toMatchSnapshot()
    })

    test('buttons', async () => {
        const container = simulate.render(id)
        container.attach(document.createElement('parent-wrapper'))

        const [cancelBtn, confirmBtn] = container.querySelectorAll('.dialog >>> .weui-dialog__btn')

        cancelBtn.dispatchEvent('tap')
        await simulate.sleep(10)
        expect(container.data.buttonValue).toBe('取消')

        confirmBtn.dispatchEvent('tap')
        await simulate.sleep(10)
        expect(container.data.buttonValue).toBe('确定')
    })

    test('close', async () => {
        const container = simulate.render(id)
        container.attach(document.createElement('parent-wrapper'))

        container.querySelector('.dialog >>> .weui-mask').dispatchEvent('tap')
        await simulate.sleep(10)
        expect(container.data.closed).toBe(true)
        expect(container.toJSON()).toMatchSnapshot()
    })
})


================================================
FILE: src/components/dialog/__test__/index.js
================================================
Component({
    data: {
        buttonValue: '',
        closed: false,
        buttons: [{ text: '取消' }, { text: '确定' }]
    },
    methods: {
        buttontap(e) {
            this.setData({
                buttonValue: e.detail.item.text
            })
        },
        close(e) {
            this.setData({
                closed: true
            })
        }
    }
})


================================================
FILE: src/components/dialog/__test__/index.json
================================================
{
    "component": true,
    "usingComponents": {
        "mp-dialog": "..//dialog"
    }
}

================================================
FILE: src/components/dialog/__test__/index.wxml
================================================
<mp-dialog
    class="dialog"
    title="dialog title"
    show="{{!closed}}"
    buttons="{{buttons}}"
    bindbuttontap="buttontap"
    bindclose="close"
>
</mp-dialog>

================================================
FILE: src/components/dialog/dialog.json
================================================
{
  "component": true,
  "styleIsolation": "apply-shared",
  "usingComponents": {}
}

================================================
FILE: src/components/dialog/dialog.less
================================================
.weui-dialog__root {
  position: relative;
  z-index: 1000;
}

.weui-dialog__title:focus {
  outline: none;
}



// FIXME: skyline animation 暂有 scope 的 bug
.weui-animate-fade-in {
  animation: weuiFadeIn ease 0.3s forwards;
}

@keyframes weuiFadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.weui-animate-fade-out {
  animation: weuiFadeOut ease 0.3s forwards;
}

@keyframes weuiFadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

================================================
FILE: src/components/dialog/dialog.ts
================================================
Component({
    options: {
        virtualHost: true,
        multipleSlots: true // 在组件定义时的选项中启用多slot支持
    },
    properties: {
        title: {
            // 弹窗标题,也可以通过 slot 自定义
            type: String,
            value: ''
        },
        extClass: {
            // 弹窗 class
            type: String,
            value: ''
        },
        maskClosable: {
            type: Boolean,
            value: true
        },
        mask: {
            // 是否需要 遮罩层
            type: Boolean,
            value: true
        },
        show: {
            // 是否开启弹窗
            type: Boolean,
            value: false,
            observer: '_showChange'
        },
        buttons: {
            type: Array,
            value: [] // {text, extClass}
        },
        rootPortal: {
            // 是否使用 root-portal
            type: Boolean,
            value: false
        }
    },
    data: {
        wrapperShow: false,
        innerShow: false
    },
    ready() {
        const buttons = this.data.buttons
        const len = buttons.length
        buttons.forEach((btn, index) => {
            if (len === 1) {
                btn.className = 'weui-dialog__btn_primary'
            } else if (index === 0) {
                btn.className = 'weui-dialog__btn_default'
            } else {
                btn.className = 'weui-dialog__btn_primary'
            }
        })
        this.setData({
            buttons
        })
        this._showChange(this.data.show)
    },
    methods: {
        _showChange(show) {
            if (show) {
                this.setData({
                    wrapperShow: true,
                    innerShow: true
                })
            } else {
                this.setData({ innerShow: false })
                setTimeout(() => {
                    this.setData({ wrapperShow: false })
                }, 300)
            }
        },
        buttonTap(e) {
            const { index } = e.currentTarget.dataset
            this.triggerEvent('buttontap', { index, item: this.data.buttons[index] }, {})
        },
        close() {
            const data: any = this.data
            if (!data.maskClosable) return
            this.setData({
                show: false
            })
            this.triggerEvent('close', {}, {})
        },
        stopEvent() {}
    }
})


================================================
FILE: src/components/dialog/dialog.wxml
================================================
<template name="body">
  <view class="weui-dialog {{extClass}} {{innerShow ? 'weui-animate-fade-in' : 'weui-animate-fade-out'}}" catchtap="stopEvent">
    <view class="weui-dialog__hd">
      <view class="weui-dialog__title" tabindex="0">{{title}}
        <slot name="title"></slot>
      </view>
    </view>
    <view class="weui-dialog__bd">
      <slot></slot>
    </view>
    <view class="weui-dialog__ft">
      <block wx:if="{{buttons && buttons.length}}">
        <view
          wx:for="{{buttons}}"
          wx:key="index"
          class="weui-dialog__btn {{item.className}} {{item.extClass}}"
          hover-class="weui-active"
          data-index="{{index}}"
          bindtap="buttonTap"
          aria-role="button"
        >{{item.text}}</view>
      </block>
      <slot name="footer" wx:else></slot>
    </view>
  </view>
  <view
    wx:if="{{mask}}"
    bindtap="close"
    class="weui-mask {{maskClass}} {{innerShow ? 'weui-animate-fade-in' : 'weui-animate-fade-out'}}"
    aria-role="button"
    aria-label="关闭"
  ></view>
</template>

<root-portal enable="{{true}}" wx:if="{{rootPortal && wrapperShow}}">
  <view
    aria-role="dialog"
    aria-modal="true"
    class="root-portal-box weui-dialog__root"
  >
    <template is="body" data="{{title, maskClosable, maskClass, extClass, mask, buttons, innerShow}}" />
  </view>
</root-portal>
<view
  wx:elif="{{!rootPortal && wrapperShow}}"
  aria-role="dialog"
  aria-modal="true"
>
  <template is="body" data="{{title, maskClosable, maskClass, extClass, mask, buttons, innerShow}}" />
</view>


================================================
FILE: src/components/form/__test__/__snapshots__/form.test.ts.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`form basic 1`] = `
<main>
  <mp-form
    id="form"
  >
    <wx-view
      class=""
    >
      <mp-cells>
        <wx-view
          ariaRole=""
          class=" weui-cells__group  weui-cells_radio"
        >
          <wx-view
            class="weui-cells__title"
          >
            单选列表项
          </wx-view>
          <wx-view
            class="weui-cells weui-cells_after-title "
          >
            <mp-checkbox-group
              bind:change="radioChange"
            >
              <wx-radio-group
                class=""
                bind:change="checkedChange"
              >
                <mp-checkbox>
                  <wx-label
                    bind:tap="checkedChange"
                  >
                    <mp-cell>
                      <wx-view
                        ariaRole=""
                        class="weui-cell  weui-check__label   ^weui-cell_radio  "
                        hoverClass="weui-active"
                        bind:tap="navigateTo"
                      >
                        <wx-view
                          class="weui-cell__bd"
                        >
                          <wx-view>
                            cell standard
                          </wx-view>
                        </wx-view>
                        <wx-view
                          class="weui-cell__ft weui-cell__ft_in-access "
                        >
                          <wx-view
                            slot="footer"
                          >
                            <wx-radio
                              checked="{{true}}"
                              class="weui-check"
                              color=""
                              disabled="{{false}}"
                              value="0"
                            />
                            <wx-view
                              class="weui-icon-checked checkbox--weui-icon-checked weui-icon-is-checked"
                            />
                          </wx-view>
                        </wx-view>
                      </wx-view>
                    </mp-cell>
                  </wx-label>
                </mp-checkbox>
                <mp-checkbox>
                  <wx-label
                    bind:tap="checkedChange"
                  >
                    <mp-cell>
                      <wx-view
                        ariaRole=""
                        class="weui-cell  weui-check__label weui-cell_wxss  ^weui-cell_radio  "
                        hoverClass="weui-active"
                        bind:tap="navigateTo"
                      >
                        <wx-view
                          class="weui-cell__bd"
                        >
                          <wx-view>
                            cell standard
                          </wx-view>
                        </wx-view>
                        <wx-view
                          class="weui-cell__ft weui-cell__ft_in-access "
                        >
                          <wx-view
                            slot="footer"
                          >
                            <wx-radio
                              checked="{{false}}"
                              class="weui-check"
                              color=""
                              disabled="{{false}}"
                              value="1"
                            />
                            <wx-view
                              class="weui-icon-checked checkbox--weui-icon-checked "
                            />
                          </wx-view>
                        </wx-view>
                      </wx-view>
                    </mp-cell>
                  </wx-label>
                </mp-checkbox>
              </wx-radio-group>
            </mp-checkbox-group>
          </wx-view>
        </wx-view>
      </mp-cells>
      <mp-cells>
        <wx-view
          ariaRole=""
          class=" weui-cells__group  weui-cells_checkbox"
        >
          <wx-view
            class="weui-cells__title"
          >
            复选列表项
          </wx-view>
          <wx-view
            class="weui-cells weui-cells_after-title weui-cells_checkbox"
          >
            <mp-checkbox-group
              bind:change="checkboxChange"
            >
              <wx-checkbox-group
                class=""
                bind:change="checkedChange"
              >
                <mp-checkbox>
                  <wx-label
                    bind:tap="checkedChange"
                  >
                    <mp-cell>
                      <wx-view
                        ariaRole=""
                        class="weui-cell  weui-check__label   ^weui-cell_checkbox  "
                        hoverClass="weui-active"
                        bind:tap="navigateTo"
                      >
                        <wx-view
                          class="weui-cell__hd "
                        >
                          <wx-view
                            slot="icon"
                          >
                            <wx-checkbox
                              checked="{{true}}"
                              class="weui-check"
                              color=""
                              disabled="{{false}}"
                              value="0"
                            />
                            <wx-view
                              class="weui-icon-checked checkbox--weui-icon-checked weui-icon-multi-is-checked"
                            />
                          </wx-view>
                        </wx-view>
                        <wx-view
                          class="weui-cell__bd"
                        >
                          <wx-view>
                            standard is dealt for u.
                          </wx-view>
                        </wx-view>
                      </wx-view>
                    </mp-cell>
                  </wx-label>
                </mp-checkbox>
                <mp-checkbox>
                  <wx-label
                    bind:tap="checkedChange"
                  >
                    <mp-cell>
                      <wx-view
                        ariaRole=""
                        class="weui-cell  weui-check__label weui-cell_wxss  ^weui-cell_checkbox  "
                        hoverClass="weui-active"
                        bind:tap="navigateTo"
                      >
                        <wx-view
                          class="weui-cell__hd "
                        >
                          <wx-view
                            slot="icon"
                          >
                            <wx-checkbox
                              checked="{{false}}"
                              class="weui-check"
                              color=""
                              disabled="{{false}}"
                              value="1"
                            />
                            <wx-view
                              class="weui-icon-checked checkbox--weui-icon-checked "
                            />
                          </wx-view>
                        </wx-view>
                        <wx-view
                          class="weui-cell__bd"
                        >
                          <wx-view>
                            standard is dealicient for u.
                          </wx-view>
                        </wx-view>
                      </wx-view>
                    </mp-cell>
                  </wx-label>
                </mp-checkbox>
              </wx-checkbox-group>
            </mp-checkbox-group>
          </wx-view>
        </wx-view>
      </mp-cells>
      <mp-cells>
        <wx-view
          ariaRole=""
          class=" weui-cells__group  "
        >
          <wx-view
            class="weui-cells__title"
          >
            表单
          </wx-view>
          <wx-view
            class="weui-cells weui-cells_after-title "
          >
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell     weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    class="weui-label"
                  >
                    姓名
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-input
                    class="weui-input"
                    data-field="name"
                    placeholder="请输入姓名"
                    bind:input="formInputChange"
                  />
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                />
              </wx-view>
            </mp-cell>
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell   weui-cell_wxss  weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    class="weui-label"
                  >
                    qq
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-input
                    class="weui-input"
                    data-field="qq"
                    placeholder="请输入qq"
                    bind:input="formInputChange"
                  />
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                />
              </wx-view>
            </mp-cell>
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell   weui-cell_vcode weui-cell_wxss  weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    class="weui-label"
                  >
                    手机号
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-input
                    class="weui-input"
                    data-field="mobile"
                    placeholder="请输入手机号"
                    bind:input="formInputChange"
                  />
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                >
                  <wx-button
                    class="weui-vcode-btn"
                    slot="footer"
                    type="default"
                  >
                    获取验证码
                  </wx-button>
                </wx-view>
              </wx-view>
            </mp-cell>
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell   weui-cell_wxss  weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    class="weui-label"
                  >
                    日期
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-picker
                    data-field="date"
                    end="2017-09-01"
                    mode="date"
                    start="2015-09-01"
                    value="2016-09-01"
                    bind:change="bindDateChange"
                  >
                    <wx-view
                      class="weui-input"
                    >
                      2016-09-01
                    </wx-view>
                  </wx-picker>
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                />
              </wx-view>
            </mp-cell>
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell   weui-cell_vcode weui-cell_wxss  weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    class="weui-label"
                  >
                    验证码
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-input
                    class="weui-input"
                    data-field="vcode"
                    placeholder="请输入验证码"
                    bind:input="formInputChange"
                  />
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                >
                  <wx-image
                    class="weui-vcode-img"
                    slot="footer"
                    src="../images/vcode.jpg"
                    style="width: 108px"
                  />
                </wx-view>
              </wx-view>
            </mp-cell>
          </wx-view>
          <wx-view
            class="weui-cells__tips"
          >
            底部说明文字底部说明文字
          </wx-view>
        </wx-view>
      </mp-cells>
      <mp-cells>
        <wx-view
          ariaRole=""
          class=" weui-cells__group  "
        >
          <wx-view
            class="weui-cells__title"
          >
            提交后表单项报错
          </wx-view>
          <wx-view
            class="weui-cells weui-cells_after-title "
          >
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell     weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    class="weui-label"
                  >
                    卡号
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-input
                    class="weui-input"
                    data-field="idcard"
                    placeholder="请输入卡号"
                    bind:input="formInputChange"
                  />
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                />
              </wx-view>
            </mp-cell>
          </wx-view>
        </wx-view>
      </mp-cells>
      <mp-cells>
        <wx-view
          ariaRole=""
          class=" weui-cells__group  "
        >
          <wx-view
            class="weui-cells__title"
          >
            开关
          </wx-view>
          <wx-view
            class="weui-cells weui-cells_after-title "
          >
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell     weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    class="weui-label"
                  >
                    标题文字
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                />
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                >
                  <wx-switch
                    checked="{{true}}"
                    slot="footer"
                  />
                </wx-view>
              </wx-view>
            </mp-cell>
          </wx-view>
        </wx-view>
      </mp-cells>
      <mp-cells>
        <wx-view
          ariaRole=""
          class=" weui-cells__group  "
        >
          <wx-view
            class="weui-cells__title"
          >
            文本框
          </wx-view>
          <wx-view
            class="weui-cells weui-cells_after-title "
          >
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell     weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    class="weui-label"
                  >
                    标题文字
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-input
                    class="weui-input"
                    placeholder="请输入文本"
                  />
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                />
              </wx-view>
            </mp-cell>
          </wx-view>
        </wx-view>
      </mp-cells>
      <mp-cells>
        <wx-view
          ariaRole=""
          class=" weui-cells__group  "
        >
          <wx-view
            class="weui-cells__title"
          >
            文本域
          </wx-view>
          <wx-view
            class="weui-cells weui-cells_after-title "
          >
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell     weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-textarea
                    class="weui-textarea"
                    placeholder="请输入文本"
                    style="height: 3.3em"
                  />
                  <wx-view
                    class="weui-textarea-counter"
                  >
                    0/200
                  </wx-view>
                </wx-view>
              </wx-view>
            </mp-cell>
          </wx-view>
        </wx-view>
      </mp-cells>
      <mp-cells>
        <wx-view
          ariaRole=""
          class=" weui-cells__group  "
        >
          <wx-view
            class="weui-cells__title"
          >
            选择
          </wx-view>
          <wx-view
            class="weui-cells weui-cells_after-title "
          >
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell  weui-cell_select weui-cell_select-before   weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    slot="title"
                    style="width: 105px"
                  >
                    <wx-picker
                      range="{{
                        Array [
                          "+86",
                          "+80",
                          "+84",
                          "+87",
                        ]
                      }}"
                      value="{{0}}"
                      bind:change="bindCountryCodeChange"
                    >
                      <wx-view
                        class="weui-select"
                      >
                        +86
                      </wx-view>
                    </wx-picker>
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-input
                    class="weui-input"
                    placeholder="请输入号码"
                  />
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                />
              </wx-view>
            </mp-cell>
          </wx-view>
        </wx-view>
      </mp-cells>
      <mp-cells>
        <wx-view
          ariaRole=""
          class=" weui-cells__group  "
        >
          <wx-view
            class="weui-cells__title"
          >
            选择
          </wx-view>
          <wx-view
            class="weui-cells weui-cells_after-title "
          >
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell  weui-cell_select   weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-picker
                    range="{{
                      Array [
                        "微信号",
                        "QQ",
                        "Email",
                      ]
                    }}"
                    value="{{0}}"
                    bind:change="bindAccountChange"
                  >
                    <wx-view
                      class="weui-select"
                    >
                      微信号
                    </wx-view>
                  </wx-picker>
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                />
              </wx-view>
            </mp-cell>
            <mp-cell>
              <wx-view
                ariaRole=""
                class="weui-cell  weui-cell_select weui-cell_select-after weui-cell_wxss  weui-cell-inform"
                hoverClass=""
                bind:tap="navigateTo"
              >
                <wx-view
                  class="weui-cell__hd "
                >
                  <wx-view
                    class="weui-label"
                    slot="title"
                  >
                    国家/地区
                  </wx-view>
                </wx-view>
                <wx-view
                  class="weui-cell__bd"
                >
                  <wx-picker
                    range="{{
                      Array [
                        "中国",
                        "美国",
                        "英国",
                      ]
                    }}"
                    value="{{0}}"
                    bind:change="bindCountryChange"
                  >
                    <wx-view
                      class="weui-select"
                    >
                      中国
                    </wx-view>
                  </wx-picker>
                </wx-view>
                <wx-view
                  class="weui-cell__ft weui-cell__ft_in-access "
                />
              </wx-view>
            </mp-cell>
          </wx-view>
        </wx-view>
      </mp-cells>
      <wx-button
        class="submit"
        bind:tap="submitForm"
      >
        提交
      </wx-button>
    </wx-view>
  </mp-form>
</main>
`;


================================================
FILE: src/components/form/__test__/comp/index.json
================================================
{
    "component": true,
    "usingComponents": {
        "mp-cells": "../../../cells/cells",
        "mp-cell": "../../../cell/cell",
        "mp-checkbox": "../../../checkbox/checkbox",
        "mp-checkbox-group": "../../../checkbox-group/checkbox-group",
        "mp-form": "../../../form/form"
    }
}


================================================
FILE: src/components/form/__test__/comp/index.ts
================================================
Component({
    data: {
        radioItems: [
            { name: 'cell standard', value: '0', checked: true },
            { name: 'cell standard', value: '1' }
        ],
        checkboxItems: [
            { name: 'standard is dealt for u.', value: '0', checked: true },
            { name: 'standard is dealicient for u.', value: '1' }
        ],
        items: [
            { name: 'USA', value: '美国' },
            { name: 'CHN', value: '中国', checked: 'true' },
            { name: 'BRA', value: '巴西' },
            { name: 'JPN', value: '日本' },
            { name: 'ENG', value: '英国' },
            { name: 'TUR', value: '法国' }
        ],

        date: '2016-09-01',
        time: '12:01',

        countryCodes: ['+86', '+80', '+84', '+87'],
        countryCodeIndex: 0,

        countries: ['中国', '美国', '英国'],
        countryIndex: 0,

        accounts: ['微信号', 'QQ', 'Email'],
        accountIndex: 0,

        formData: {},
        rules: [
            {
                name: 'radio',
                rules: { required: false, message: '单选列表是必选项' }
            },
            {
                name: 'checkbox',
                rules: { required: true, message: '多选列表是必选项' }
            },
            {
                name: 'name',
                rules: { required: true, message: '请输入姓名' }
            },
            {
                name: 'qq',
                rules: { required: true, message: 'qq必填' }
            },
            {
                name: 'mobile',
                rules: [
                    { required: true, message: 'mobile必填' },
                    { mobile: true, message: 'mobile格式不对' }
                ]
            },
            {
                name: 'vcode',
                rules: { required: true, message: '验证码必填' }
            },
            {
                name: 'idcard',
                rules: {
                    validator: function (rule, value, param, modeels) {
                        if (!value || value.length !== 18) {
                            return 'idcard格式不正确'
                        }
                    }
                }
            }
        ]
    },
    methods: {
        radioChange: function (e) {
            // console.log('radio发生change事件,携带value值为:', e.detail.value)

            const radioItems = this.data.radioItems
            for (let i = 0, len = radioItems.length; i < len; ++i) {
                radioItems[i].checked = radioItems[i].value == e.detail.value
            }

            this.setData({
                radioItems: radioItems,
                [`formData.radio`]: e.detail.value
            })
        },
        checkboxChange: function (e) {
            // console.log('checkbox发生change事件,携带value值为:', e.detail.value)

            const checkboxItems = this.data.checkboxItems,
                values = e.detail.value
            for (let i = 0, lenI = checkboxItems.length; i < lenI; ++i) {
                checkboxItems[i].checked = false

                for (let j = 0, lenJ = values.length; j < lenJ; ++j) {
                    if (checkboxItems[i].value == values[j]) {
                        checkboxItems[i].checked = true
                        break
                    }
                }
            }

            this.setData({
                checkboxItems: checkboxItems,
                [`formData.checkbox`]: e.detail.value
            })
        },
        bindDateChange: function (e) {
            this.setData({
                date: e.detail.value,
                [`formData.date`]: e.detail.value
            })
        },
        formInputChange(e) {
            const { field } = e.currentTarget.dataset
            this.setData({
                [`formData.${field}`]: e.detail.value
            })
        },
        bindTimeChange: function (e) {
            this.setData({
                time: e.detail.value
            })
        },
        bindCountryCodeChange: function (e) {
            // console.log('picker country code 发生选择改变,携带值为', e.detail.value)

            this.setData({
                countryCodeIndex: e.detail.value
            })
        },
        bindCountryChange: function (e) {
            // console.log('picker country 发生选择改变,携带值为', e.detail.value)

            this.setData({
                countryIndex: e.detail.value
            })
        },
        bindAccountChange: function (e) {
            // console.log('picker account 发生选择改变,携带值为', e.detail.value)

            this.setData({
                accountIndex: e.detail.value
            })
        },
        bindAgreeChange: function (e) {
            this.setData({
                isAgree: !!e.detail.value.length
            })
        },
        submitForm() {
            this.selectComponent('#form').validate((valid, errors) => {
                // console.log('valid', valid, errors)
                if (!valid) {
                    const firstError = Object.keys(errors)
                    if (firstError.length) {
                        this.setData({
                            error: errors[firstError[0]].message
                        })
                    }
                } else {
                    wx.showToast({
                        title: '校验通过'
                    })
                }
            })
            // this.selectComponent('#form').validateField('mobile', (valid, errors) => {
            //     console.log('valid', valid, errors)
            // })
        }
    }
})


================================================
FILE: src/components/form/__test__/comp/index.wxml
================================================
<mp-form id="form" rules="{{rules}}" models="{{formData}}">
    <mp-cells title="单选列表项">
        <mp-checkbox-group prop="radio" multi="{{false}}" bindchange="radioChange">
            <mp-checkbox wx:for="{{radioItems}}" wx:key="value" label="{{item.name}}" value="{{item.value}}" checked="{{item.checked}}"></mp-checkbox>
        </mp-checkbox-group>
    </mp-cells>
    <mp-cells title="复选列表项">
        <mp-checkbox-group prop="checkbox" multi="{{true}}" bindchange="checkboxChange">
            <mp-checkbox wx:for="{{checkboxItems}}" wx:key="value" label="{{item.name}}" value="{{item.value}}" checked="{{item.checked}}"></mp-checkbox>
        </mp-checkbox-group>
    </mp-cells>
    <mp-cells title="表单" footer="底部说明文字底部说明文字">
        <mp-cell prop="name" title="姓名" ext-class="">
            <input bindinput="formInputChange" data-field="name" class="weui-input" placeholder="请输入姓名" />
        </mp-cell>
        <mp-cell prop="qq" title="qq" ext-class="">
            <input bindinput="formInputChange" data-field="qq" class="weui-input" placeholder="请输入qq" />
        </mp-cell>
        <mp-cell prop="mobile" title="手机号" ext-class=" weui-cell_vcode">
            <input bindinput="formInputChange" data-field="mobile" class="weui-input" placeholder="请输入手机号" />
            <button slot="footer" type="default" class="weui-vcode-btn">获取验证码</button>
        </mp-cell>
        <mp-cell prop="date" title="日期" ext-class="">
            <picker data-field="date" mode="date" value="{{date}}" start="2015-09-01" end="2017-09-01" bindchange="bindDateChange">
                <view class="weui-input">{{date}}</view>
            </picker>
        </mp-cell>
        <mp-cell prop="vcode" title="验证码" ext-class=" weui-cell_vcode">
            <input bindinput="formInputChange" data-field="vcode" class="weui-input" placeholder="请输入验证码" />
            <image slot="footer" class="weui-vcode-img" src="../images/vcode.jpg" style="width: 108px"></image>
        </mp-cell>
    </mp-cells>
    <mp-cells title="提交后表单项报错">
        <mp-cell show-error prop="idcard" title="卡号" ext-class="">
            <input bindinput="formInputChange" data-field="idcard" class="weui-input" placeholder="请输入卡号" />
        </mp-cell>
    </mp-cells>
    <mp-cells title="开关">
        <mp-cell title="标题文字" ext-class="">
            <switch slot="footer" checked />
        </mp-cell>
    </mp-cells>
    <mp-cells title="文本框">
        <mp-cell title="标题文字" ext-class="">
            <input class="weui-input" placeholder="请输入文本" />
        </mp-cell>
    </mp-cells>
    <mp-cells title="文本域">
        <mp-cell has-header="{{false}}" has-footer="{{false}}" title="" ext-class="">
            <textarea class="weui-textarea" placeholder="请输入文本" style="height: 3.3em" />
            <view class="weui-textarea-counter">0/200</view>
        </mp-cell>
    </mp-cells>
    <mp-cells title="选择">
        <mp-cell ext-class="weui-cell_select weui-cell_select-before">
            <view slot="title" style="width: 105px">
                <picker bindchange="bindCountryCodeChange" value="{{countryCodeIndex}}" range="{{countryCodes}}">
                    <view class="weui-select">{{countryCodes[countryCodeIndex]}}</view>
                </picker>
            </view>
            <input class="weui-input" placeholder="请输入号码" />
        </mp-cell>
    </mp-cells>
    <mp-cells title="选择">
        <mp-cell has-header="{{false}}" ext-class="weui-cell_select">
            <picker bindchange="bindAccountChange" value="{{accountIndex}}" range="{{accounts}}">
                <view class="weui-select">{{accounts[accountIndex]}}</view>
            </picker>
        </mp-cell>
        <mp-cell ext-class="weui-cell_select weui-cell_select-after">
            <view slot="title" class="weui-label">国家/地区</view>
            <picker bindchange="bindCountryChange" value="{{countryIndex}}" range="{{countries}}">
                <view class="weui-select">{{countries[countryIndex]}}</view>
            </picker>
        </mp-cell>
    </mp-cells>
    <button class="submit" bindtap="submitForm">提交</button>
</mp-form>

================================================
FILE: src/components/form/__test__/form-validator.test.ts
================================================
import FormValidator from '../form-validator'

describe('form-validator', () => {
    test('basic usage', async () => {
        const formValidator = new FormValidator(
            { username: 'bar' },
            {
                username: [
                    {
                        required: true
                    }
                ]
            }
        )
        const { isValid, errors } = await formValidator.validate()
        expect(isValid).toBe(true)
        expect(errors).toBe(undefined)
    })

    test('invalid', async () => {
        const formValidator = new FormValidator(
            { username: 'bar', password: '123', repeatPassword: '' },
            {
                username: [
                    {
                        required: true
                    },
                    {
                        minlength: 3
                    }
                ],
                password: [{ required: true }, { minlength: 6 }],
                repeatPassword: [{ required: true }, { equalTo: 'password' }]
            }
        )
        const { isValid, errors } = await formValidator.validate()
        expect(isValid).toBe(false)
        expect(errors).toStrictEqual({
            password: { message: '长度最少为6', rule: { minlength: 6, name: 'password' } },
            repeatPassword: {
                message: 'repeatPassword必填',
                rule: { name: 'repeatPassword', required: true }
            }
        })
    })
})


================================================
FILE: src/components/form/__test__/form.test.ts
================================================
import path from 'path'
import simulate from 'miniprogram-simulate'

describe('form', () => {
    let id: string

    beforeAll(() => {
        id = simulate.load(path.resolve(__dirname, './comp/index'), { less: true })
    })

    test('basic', async () => {
        const comp = simulate.render(id)
        comp.attach(document.createElement('parent-wrapper'))
        await simulate.sleep(0)
        expect(comp.toJSON()).toMatchSnapshot()

        // const form = comp.querySelector('#form')
        const submit = comp.querySelector('.submit')

        submit.dispatchEvent('tap')
        await simulate.sleep(0)
        expect(comp.data.error).toBe('多选列表是必选项')
    })
})


================================================
FILE: src/components/form/__test__/validator.test.ts
================================================
import Validators from '../validator'

describe('required', () => {
    test('success', () => {
        expect(Validators.required({ name: '姓名', required: true }, '张三')).toBe('')
    })

    test('empty should fail', () => {
        expect(Validators.required({ name: '姓名', required: true }, '')).toBe('姓名必填')
        expect(Validators.required({ name: '姓名', required: true }, null)).toBe('姓名必填')
        expect(Validators.required({ name: '姓名', required: true }, undefined)).toBe('姓名必填')
    })

    test('0 or false should success', () => {
        expect(Validators.required({ name: '姓名', required: true }, 0)).toBe('')
        expect(Validators.required({ name: '姓名', required: true }, false)).toBe('')
    })
})

describe('minlength', () => {
    test('success', () => {
        expect(Validators.minlength({ name: '姓名', minlength: 2 }, '张三')).toBe('')
    })

    test('fail', () => {
        expect(Validators.minlength({ name: '姓名', minlength: 2 }, '张')).toBe('长度最少为2')
    })
})

describe('maxlength', () => {
    test('success', () => {
        expect(Validators.maxlength({ name: '姓名', maxlength: 5 }, '张三张三张')).toBe('')
    })

    test('fail', () => {
        expect(Validators.maxlength({ name: '姓名', maxlength: 5 }, '张张张张张张')).toBe(
            '长度最大为5'
        )
    })
})

describe('rangelength', () => {
    test('success', () => {
        expect(Validators.rangelength({ name: '姓名', rangelength: [2, 3] }, '张三')).toBe('')
    })

    test('fail', () => {
        expect(Validators.rangelength({ name: '姓名', rangelength: [2, 3] }, '张')).toBe(
            '长度在2和3之间'
        )
        expect(Validators.rangelength({ name: '姓名', rangelength: [2, 3] }, '张三张三')).toBe(
            '长度在2和3之间'
        )
    })
})

describe('min', () => {
    test('success', () => {
        expect(Validators.min({ name: '年龄', min: 0 }, 0)).toBe('')
    })

    test('fail', () => {
        expect(Validators.min({ name: '年龄', min: 0 }, -1)).toBe('值最小为0')
    })
})

describe('max', () => {
    test('success', () => {
        expect(Validators.max({ name: '年龄', max: 100 }, 100)).toBe('')
    })

    test('fail', () => {
        expect(Validators.max({ name: '年龄', max: 100 }, 101)).toBe('值最大为100')
    })
})

describe('range', () => {
    test('success', () => {
        expect(Validators.range({ name: '年龄', range: [0, 100] }, 0)).toBe('')
        expect(Validators.range({ name: '年龄', range: [0, 100] }, 100)).toBe('')
    })

    test('fail', () => {
        expect(Validators.range({ name: '年龄', range: [0, 100] }, -1)).toBe('值的范围为0和100之间')
        expect(Validators.range({ name: '年龄', range: [0, 100] }, 101)).toBe(
            '值的范围为0和100之间'
        )
    })
})

describe('mobile', () => {
    test('success', () => {
        expect(Validators.mobile({ name: '手机号' }, '12345678901')).toBe('')
    })
})

describe('email', () => {
    test('success', () => {
        expect(Validators.email({ name: '邮箱' }, '123@123.com')).toBe('')
    })

    test('fail', () => {
        expect(Validators.email({ name: '邮箱' }, '123@123')).toBe('请输入正确的电子邮件')
    })
})

describe('url', () => {
    test('success', () => {
        expect(Validators.url({ name: 'url' }, 'https://qq.com')).toBe('')
        expect(Validators.url({ name: 'url' }, 'weixin://qq.com')).toBe('')
    })

    test('fail', () => {
        expect(Validators.url({ name: 'url' }, 'https:/qq.com')).toBe('请输入正确的URL地址')
        expect(Validators.url({ name: 'url' }, 'weixin:// qq.com')).toBe('请输入正确的URL地址')
    })
})

describe('equalTo', () => {
    test('success', () => {
        expect(Validators.equalTo({ equalTo: 'b' }, 'foo', null, { b: 'foo' })).toBe('')
    })

    test('success', () => {
        expect(Validators.equalTo({ equalTo: 'b', name: 'B' }, 'foo', null, { b: 'bar' })).toBe(
            '值和字段B不相等'
        )
    })
})

describe('bytelength', () => {
    test('success', () => {
        expect(Validators.bytelength({ param: 3 }, 'foo', null)).toBe('')
        expect(Validators.bytelength({ param: 2 }, '蛤', null)).toBe('')
    })

    test('fail', () => {
        expect(Validators.bytelength({ param: 2 }, 'f蛤', null)).toBe('最多只能输入2个字')
    })
})


================================================
FILE: src/components/form/form-validator.ts
================================================
import Validator from './validator'
import { diff } from '../utils/object'

const toString = Object.prototype.toString
const validateSingleRule = (rule: any, value: any, param: any = null, models = null) => {
    let message = ''
    const ruleKeys = Object.keys(rule)

    for (let i = 0, l = ruleKeys.length; i < l; ++i) {
        const ruleKey = ruleKeys[i]
        if (ruleKey === 'name' || ruleKey === 'message') continue
        const validateMethod =
            typeof rule.validator !== 'undefined' ? rule.validator : Validator[ruleKey]
        if (typeof validateMethod === 'function') {
            message = validateMethod(rule, value, param, models)
            if (message) {
                return message
            }
        }
    }
    return message
}
class FormValidator {
    models: {}

    rules: {}

    errors: {}

    constructor(models, rules) {
        this.models = models
        this.rules = rules
        this.errors = {}
    }

    validate(): Promise<{ isValid: boolean; errors: any }>
    validate(cb: (isValid: boolean, errors: any) => void): void
    validate(cb?: any) {
        return new Promise((resolve) => {
            let failCount = 0
            const errors = this.errors
            const models = this.models
            // let errorChanged = false
            Object.keys(this.rules).forEach((fieldName) => {
                const oldError = errors[fieldName]
                this._innerValidateField(fieldName, models[fieldName], (valid, newError) => {
                    if (!valid) failCount++
                    if (diff(oldError, newError)) {
                        errors[fieldName] = newError
                        // errorChanged = true
                    }
                })
            })
            const keys = Object.keys(errors)
            keys.forEach((key) => {
                if (!errors[key]) delete errors[key]
            })
            // 先支持同步的接口吧
            resolve({ isValid: !failCount, errors: failCount ? errors : undefined })
            cb && cb(!failCount, failCount ? errors : undefined)
        })
    }

    validateField(name: string, value: any): Promise<{ valid: boolean; error: any }>
    validateField(name: string, value: any, cb: (isValid: boolean, errors: any[]) => void): void
    validateField(name, value, cb?) {
        return new Promise((resolve) => {
            this._innerValidateField(name, value, (valid, error) => {
                const errObj = {}
                errObj[name] = error
                resolve({ valid, error: valid ? undefined : error })
                cb && cb(valid, valid ? undefined : errObj)
                const oldError = this.errors[name]
                const errorChanged = diff(oldError, error)
                if (errorChanged) {
                    if (!error) delete this.errors[name]
                    this.errors[name] = error
                }
            })
        })
    }

    _innerValidateField(name, value, cb) {
        const rules = this.rules[name]
        if (!rules) {
            console.warn(`[form-validator] rule name ${name} not exists.`)
            cb(true)
            return
        }
        // 处理参数
        if (typeof value === 'function') {
            cb = value
            value = undefined
        }
        let isFail = false
        const models = this.models
        if (toString.call(rules) === '[object Array]') {
            rules.forEach((rule) => {
                rule.name = name // 字段名称
                const resMessage = validateSingleRule(
                    rule,
                    value || models[name],
                    rule.param,
                    models
                )
                // 失败了直接中止
                if (resMessage && !isFail) {
                    isFail = true
                    // errors[name] = {message: resMessage}
                    const error = resMessage ? { message: resMessage, rule } : undefined
                    cb(false, error)
                }
            })
            // 成功的回调
            if (!isFail) {
                cb(!isFail)
            }
        } else {
            const rule = rules
            rule.name = name
            const resMessage = validateSingleRule(rule, value || models[name], rule.param, models)
            const error = resMessage ? { message: resMessage, rule } : undefined
            if (resMessage) {
                isFail = true
            }
            cb(!isFail, error)
        }
    }

    static addMethod(ruleName, method) {
        Validator[ruleName] = method
    }

    setModel(newModel) {
        this.models = newModel
    }

    setRules(newRules) {
        this.rules = newRules
    }
}
export default FormValidator


================================================
FILE: src/components/form/form.json
================================================
{
    "component": true,
    "styleIsolation": "apply-shared",
    "usingComponents": {}
}

================================================
FILE: src/components/form/form.ts
================================================
import FormValidator from './form-validator'
import { diffObject } from '../utils/object'

function linked(target) {
    if (target.data.prop) {
        this.data.formItems[target.data.prop] = target
    }
    if (target.setInForm) {
        target.setInForm()
    }
    if (!this.data.firstItem) {
        this.data.firstItem = target
    }
}
function unlinked(target) {
    if (target.data.prop) {
        delete this.data.formItems[target.data.prop]
    }
}

Component({
    properties: {
        models: {
            type: Object,
            value: {},
            observer: '_modelChange'
        },
        rules: {
            // 格式是[{name, rules: {}}]
            type: Array,
            value: [],
            observer: '_rulesChange'
        },
        extClass: {
            type: String,
            value: ''
        }
    },
    data: {
        errors: {},
        formItems: {},
        firstItem: null
    },
    relations: {
        '../cell/cell': {
            type: 'descendant',
            linked,
            unlinked
        },
        '../checkbox-group/checkbox-group': {
            type: 'descendant',
            linked,
            unlinked
        }
    },
    attached() {
        this.initRules()
        this.formValidator = new FormValidator(this.data.models, this.data.newRules)
    },
    methods: {
        initRules(rules) {
            const newRules = {}
            ;(rules || this.data.rules).forEach((rule) => {
                if (rule.name && rule.rules) {
                    newRules[rule.name] = rule.rules || []
                }
            })
            this.setData({ newRules })
            return newRules
        },
        _modelChange(newVal, oldVal) {
            if (!this.formValidator) {
                return newVal
            }
            // 这个必须在前面
            this.formValidator.setModel(newVal)
            const diffObj: any = diffObject(oldVal, newVal)
            if (diffObj) {
                let isValid = true
                const errors = []
                const errorMap = {}
                Object.keys(diffObj).forEach((k) => {
                    this.formValidator.validateField(k, diffObj[k], function (isValided, error) {
                        if (error && error[k]) {
                            errors.push(error[k])
                            errorMap[k] = error[k]
                        }
                        isValid = isValided
                    })
                })
                this._showErrors(diffObj, errorMap)
                this.triggerEvent(
                    isValid ? 'success' : 'fail',
                    isValid ? { trigger: 'change' } : { errors, trigger: 'change' }
                )
            }
            return newVal
        },
        _rulesChange(newVal) {
            const newRules = this.initRules(newVal)
            if (this.formValidator) {
                this.formValidator.setRules(newRules)
            }
            return newVal
        },
        _showAllErrors(errors) {
            Object.keys(this.data.newRules).forEach((k) => {
                this._showError(k, errors && errors[k])
            })
        },
        _showErrors(objs, errors) {
            Object.keys(objs).forEach((k) => {
                this._showError(k, errors && errors[k])
            })
        },
        _showError(prop, error) {
            const formItem = this.data.formItems[prop]
            if (formItem && formItem.data.showError) {
                formItem.setError(error)
            }
        },
        validate(cb) {
            return this.formValidator.validate((isValid, errors) => {
                this._showAllErrors(errors)
                const handleError = this.handleErrors(errors)
                this.triggerEvent(
                    isValid ? 'success' : 'fail',
                    isValid ? { trigger: 'validate' } : { errors: handleError, trigger: 'validate' }
                )
                cb && cb(isValid, handleError)
            })
        },
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        validateField(name, value, cb = (v, error = null) => {}) {
            return this.formValidator.validateField(name, value, (isValid, errors) => {
                this._showError(name, errors)
                const handleError = this.handleErrors(errors)
                this.triggerEvent(
                    isValid ? 'success' : 'fail',
                    isValid ? { trigger: 'validate' } : { errors: handleError, trigger: 'validate' }
                )
                cb && cb(isValid, handleError)
            })
        },
        handleErrors(errors) {
            if (errors) {
                const newErrors = []
                this.data.rules.forEach((rule) => {
                    if (errors[rule.name]) {
                        errors[rule.name].name = rule.name
                        newErrors.push(errors[rule.name])
                    }
                })
                return newErrors
            }
            return errors
        },
        addMethod(ruleName, method) {
            return this.formValidator.addMethod(ruleName, method)
        }
    }
})
export default FormValidator


================================================
FILE: src/components/form/form.wxml
================================================
<view class="{{extClass}}">
    <slot></slot>
</view>

================================================
FILE: src/components/form/validator.ts
================================================
import { sprintf } from '../utils/string'

const defaultMessage = {
    required: '%s必填',
    minlength: '长度最少为%s',
    maxlength: '长度最大为%s',
    rangelength: '长度在%s和%s之间',
    bytelength: '最多只能输入%s个字',
    min: '值最小为%s',
    max: '值最大为%s',
    range: '值的范围为%s和%s之间',
    mobile: '请输入正确的手机号',
    email: '请输入正确的电子邮件',
    url: '请输入正确的URL地址',
    equalTo: '值和字段%s不相等'
}

const isEmpty = function (val): boolean {
    if (val === 0 || val === false) return false
    return !val
}

export default {
    required(r, val): string {
        if (r.required === false) return ''
        else if (isEmpty(val)) return sprintf(r.message || defaultMessage.required, r.name)
        else return ''
    },
    minlength(r, val) {
        const minlen = r.minlength
        val = val || ''
        if (val.length < minlen) return sprintf(r.message || defaultMessage.minlength, minlen)
        else return ''
    },
    maxlength(r, val) {
        const maxlen = r.maxlength
        val = val || ''
        if (val.length > maxlen) {
            return sprintf(r.message || defaultMessage.maxlength, maxlen)
        } else {
            return ''
        }
    },
    rangelength(r, val) {
        const range = r.rangelength
        val = val || ''
        if (val.length > range[1] || val.length < range[0]) {
            return sprintf(r.message || defaultMessage.rangelength, range[0], range[1])
        } else {
            return ''
        }
    },
    min(r, val) {
        const min = r.min
        if (val < min) {
            return sprintf(r.message || defaultMessage.min, min)
        } else {
            return ''
        }
    },
    max(r, val) {
        const max = r.max
        if (val > max) {
            return sprintf(r.message || defaultMessage.max, max)
        } else {
            return ''
        }
    },
    range(r, val) {
        const range = r.range
        if (val < range[0] || val > range[1]) {
            return sprintf(r.message || defaultMessage.range, range[0], range[1])
        } else {
            return ''
        }
    },
    mobile(r, val) {
        val = val || ''
        if (r.mobile === false) {
            return ''
        } else if (val.length !== 11) {
            return sprintf(r.message || defaultMessage.mobile)
        } else {
            return ''
        }
    },
    email(r, value) {
        if (r.email === false) return ''
        // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
        // eslint-disable-next-line
    if (!/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i.test(value)) {
            return sprintf(r.message || defaultMessage.email)
        } else {
            return ''
        }
    },

    // http://docs.jquery.com/Plugins/Validation/Methods/url
    url(r, value) {
        if (r.url === false) return ''
        // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
        if (
            // eslint-disable-next-line no-useless-escape
            !/^(https?|s?ftp|weixin):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(
                value
            )
        ) {
            return r.message || defaultMessage.url
        } else {
            return ''
        }
    },
    equalTo(r, value, param, models) {
        const equalTo = r.equalTo
        if (value !== models[equalTo]) {
            return sprintf(r.message || defaultMessage.equalTo, r.name)
        } else {
            return ''
        }
    },
    bytelength(r, value, param) {
        param = r.param
        value = value || ''
        // eslint-disable-next-line no-control-regex
        const len = value.replace(/[^\x00-\xff]/g, '**').length
        if (len > param) {
            return sprintf(r.message || defaultMessage.bytelength, param)
        } else {
            return ''
        }
    }
}


================================================
FILE: src/components/form-page/__test__/__snapshots__/form-page.test.ts.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`form-page basic 1`] = `
<main>
  <mp-form-page>
    <wx-view
      class="weui-form"
    >
      <wx-view
        class="weui-form__text-area"
      >
        <wx-view
          class="weui-form__title"
        >
          表单结构
        </wx-view>
        <wx-view
          class="weui-form__desc"
        >
          展示表单页面的信息结构样式, 分别由头部区域/控件区域/提示区域/操作区域和底部信息区域组成。
        </wx-view>
      </wx-view>
      <wx-view
        class="weui-form__control-area"
      >
        <wx-view>
          <mp-cells>
            <wx-view
              ariaRole=""
              class=" weui-cells__group weui-cells__group weui-cells__group_form weui-cells_form "
            >
              <wx-view
                class="weui-cells__title"
              >
                单选列表项
              </wx-view>
              <wx-view
                class="weui-cells weui-cells_after-title "
              />
            </wx-view>
          </mp-cells>
        </wx-view>
      </wx-view>
      <wx-view
        class="weui-form__tips-area"
      />
      <wx-view
        class="weui-form__opr-area"
      >
        <wx-view
          slot="button"
        >
          <wx-button
            class="weui-btn"
            type="primary"
            bind:tap="submitForm"
          >
            确定
          </wx-button>
        </wx-view>
      </wx-view>
      <wx-view
        class="weui-form__tips-area"
      />
      <wx-view
        class="weui-form__extra-area"
      >
        <wx-view
          class="weui-footer"
        />
      </wx-view>
    </wx-view>
  </mp-form-page>
</main>
`;


================================================
FILE: src/components/form-page/__test__/form-page.test.ts
================================================
import path from 'path'
import simulate from 'miniprogram-simulate'

describe('form-page', () => {
    const id = simulate.load(path.resolve(__dirname, './index'))

    test('basic', async () => {
        const comp = simulate.render(id)
        comp.attach(document.createElement('parent-wrapper'))
        await simulate.sleep(0)
        expect(comp.toJSON()).toMatchSnapshot()
    })
})


================================================
FILE: src/components/form-page/__test__/index.js
================================================
Component({})


================================================
FILE: src/components/form-page/__test__/index.json
================================================
{
    "usingComponents": {
        "mp-form-page": "../form-page",
        "mp-cells": "../../cells/cells"
    }
}

================================================
FILE: src/components/form-page/__test__/index.wxml
================================================
<mp-form-page title="表单结构" subtitle="展示表单页面的信息结构样式, 分别由头部区域/控件区域/提示区域/操作区域和底部信息区域组成。">
    <view>
        <mp-cells title="单选列表项"></mp-cells>
    </view>
    <view slot="button">
        <button class="weui-btn" type="primary" bindtap="submitForm">确定</button>
    </view>
</mp-form-page>

================================================
FILE: src/components/form-page/form-page.json
================================================
{
  "component": true,
  "styleIsolation": "apply-shared",
  "usingComponents": {}
}

================================================
FILE: src/components/form-page/form-page.less
================================================


================================================
FILE: src/components/form-page/form-page.ts
================================================
Component({
    options: {
        multipleSlots: true
    },
    properties: {
        title: {
            // Msg 标题
            type: String,
            value: ''
        },
        subtitle: {
            // icon 的 type
            type: String,
            value: ''
        }
    },
    relations: {
        '../cells/cells': {
            type: 'descendant',
            linked(target) {
                if (!this.data.firstItem) {
                    this.data.firstItem = target
                }
                target.setOuterClass('weui-cells__group weui-cells__group_form weui-cells_form')
                if (target !== this.data.firstItem) {
                    target.setOuterClass(
                        'weui-cells__group_wxss weui-cells__group weui-cells__group_form weui-cells_form'
                    )
                }
            }
        }
    },
    data: {
        firstItem: null
    }
})


================================================
FILE: src/components/form-page/form-page.wxml
================================================
<view class="weui-form">
    <block wx:if="{{title || subtitle}}">
      <view class="weui-form__text-area">
          <view class="weui-form__title">{{title}}</view>
          <view class="weui-form__desc">{{subtitle}}</view>
      </view>
    </block>
    <block wx:else>
      <view class="weui-form__text-area">
        <slot name="title"></slot>
      </view>
    </block>
  <view class="weui-form__control-area">
    <slot></slot>
  </view>
  <view class="weui-form__tips-area">
    <slot name="tips"></slot>
  </view>
  <view class="weui-form__opr-area">
    <slot name="button"></slot>
  </view>
  <view class="weui-form__tips-area">
    <slot name="suffixtips"></slot>
  </view>
  <view class="weui-form__extra-area">
    <view class="weui-footer">
      <slot name="footer"></slot>
    </view>
  </view>
</view>



================================================
FILE: src/components/gallery/__test__/__snapshots__/gallery.test.ts.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`gallery basic 1`] = `
<mp-gallery
  delete="{{true}}"
  id="gallery"
  bind:change="onChange"
  bind:delete="onDelete"
  bind:hide="onHide"
/>
`;

exports[`gallery basic 2`] = `
<mp-gallery
  delete="{{true}}"
  id="gallery"
  bind:change="onChange"
  bind:delete="onDelete"
  bind:hide="onHide"
>
  <wx-root-portal
    enable="{{true}}"
  >
    <wx-view
      ariaModal="true"
      class="root-portal-box weui-gallery weui-animate-fade-in "
      tabindex="0"
    >
      <wx-view
        class="weui-gallery__info"
      >
        1/3
      </wx-view>
      <wx-swiper
        autoplay="{{false}}"
        class="weui-gallery__img__wrp"
        current="{{0}}"
        duration="{{500}}"
        indicatorDots="{{false}}"
        bind:change="change"
        bind:tap="hideGallery"
      >
        <wx-swiper-item>
          <wx-image
            class="weui-gallery__img"
            mode="aspectFit"
            src="http://qq.com/1.jpg"
          />
        </wx-swiper-item>
        <wx-swiper-item>
          <wx-image
            class="weui-gallery__img"
            mode="aspectFit"
            src="http://qq.com/2.jpg"
          />
        </wx-swiper-item>
        <wx-swiper-item>
          <wx-image
            class="weui-gallery__img"
            mode="aspectFit"
            src="http://qq.com/3.jpg"
          />
        </wx-swiper-item>
      </wx-swiper>
      <wx-view
        class="weui-gallery__opr"
      >
        <wx-view
          ariaLabel="删除"
          ariaRole="button"
          class="weui-gallery__del"
          hoverClass="weui-active"
          bind:tap="deleteImg"
        >
          <i
            class="weui-icon-delete weui-icon_gallery-delete"
          />
        </wx-view>
      </wx-view>
    </wx-view>
  </wx-root-portal>
</mp-gallery>
`;

exports[`gallery basic 3`] = `
<mp-gallery
  delete="{{true}}"
  id="gallery"
  bind:change="onChange"
  bind:delete="onDelete"
  bind:hide="onHide"
>
  <wx-root-portal
    enable="{{true}}"
  >
    <wx-view
      ariaModal="true"
      class="root-portal-box weui-gallery weui-animate-fade-in "
      tabindex="0"
    >
      <wx-view
        class="weui-gallery__info"
      >
        2/3
      </wx-view>
      <wx-swiper
        autoplay="{{false}}"
        class="weui-gallery__img__wrp"
        current="{{1}}"
        duration="{{500}}"
        indicatorDots="{{false}}"
        bind:change="change"
        bind:tap="hideGallery"
      >
        <wx-swiper-item>
          <wx-image
            class="weui-gallery__img"
            mode="aspectFit"
            src="http://qq.com/1.jpg"
          />
        </wx-swiper-item>
        <wx-swiper-item>
          <wx-image
            class="weui-gallery__img"
            mode="aspectFit"
            src="http://qq.com/2.jpg"
          />
        </wx-swiper-item>
        <wx-swiper-item>
          <wx-image
            class="weui-gallery__img"
            mode="aspectFit"
            src="http://qq.com/3.jpg"
          />
        </wx-swiper-item>
      </wx-swiper>
      <wx-view
        class="weui-gallery__opr"
      >
        <wx-view
          ariaLabel="删除"
          ariaRole="button"
          class="weui-gallery__del"
          hoverClass="weui-active"
          bind:tap="deleteImg"
        >
          <i
            class="weui-icon-delete weui-icon_gallery-delete"
          />
        </wx-view>
      </wx-view>
    </wx-view>
  </wx-root-portal>
</mp-gallery>
`;

exports[`gallery basic 4`] = `
<mp-gallery
  delete="{{true}}"
  id="gallery"
  bind:change="onChange"
  bind:delete="onDelete"
  bind:hide="onHide"
>
  <wx-root-portal
    enable="{{true}}"
  >
    <wx-view
      ariaModal="true"
      class="root-portal-box weui-gallery weui-animate-fade-in "
      tabindex="0"
    >
      <wx-view
        class="weui-gallery__info"
      >
        1/2
      </wx-view>
      <wx-swiper
        autoplay="{{false}}"
        class="weui-gallery__img__wrp"
        current="{{0}}"
        duration="{{500}}"
        indicatorDots="{{false}}"
        bind:change="change"
        bind:tap="hideGallery"
      >
        <wx-swiper-item>
          <wx-image
            class="weui-gallery__img"
            mode="aspectFit"
            src="http://qq.com/1.jpg"
          />
        </wx-swiper-item>
        <wx-swiper-item>
          <wx-image
            class="weui-gallery__img"
            mode="aspectFit"
            src="http://qq.com/3.jpg"
          />
        </wx-swiper-item>
      </wx-swiper>
      <wx-view
        class="weui-gallery__opr"
      >
        <wx-view
          ariaLabel="删除"
          ariaRole="button"
          class="weui-gallery__del"
          hoverClass="weui-active"
          bind:tap="deleteImg"
        >
          <i
            class="weui-icon-delete weui-icon_gallery-delete"
          />
        </wx-view>
      </wx-view>
    </wx-view>
  </wx-root-portal>
</mp-gallery>
`;

exports[`gallery basic 5`] = `
<mp-gallery
  delete="{{true}}"
  id="gallery"
  bind:change="onChange"
  bind:delete="onDelete"
  bind:hide="onHide"
>
  <wx-root-portal
    enable="{{true}}"
  >
    <wx-view
      ariaModal="true"
      class="root-portal-box weui-gallery weui-animate-fade-out "
      tabindex="0"
    >
      <wx-view
        class="weui-gallery__info"
      >
        2/0
      </wx-view>
      <wx-swiper
        autoplay="{{false}}"
        class="weui-gallery__img__wrp"
        current="{{1}}"
        duration="{{500}}"
        indicatorDots="{{false}}"
        bind:change="change"
        bind:tap="hideGallery"
      />
      <wx-view
        class="weui-gallery__opr"
      >
        <wx-view
          ariaLabel="删除"
          ariaRole="button"
          class="weui-gallery__del"
          hoverClass="weui-active"
          bind:tap="deleteImg"
        >
          <i
            class="weui-icon-delete weui-icon_gallery-delete"
          />
        </wx-view>
      </wx-view>
    </wx-view>
  </wx-root-portal>
</mp-gallery>
`;


================================================
FILE: src/components/gallery/__test__/gallery.test.ts
================================================
import path from 'path'
import simulate from 'miniprogram-simulate'

describe('gallery', () => {
    const gallery = simulate.load(path.resolve(__dirname, '../gallery'), 'mp-gallery')

    const onChange = jest.fn(function (e) {
        this.setData({
            current: e.detail.current
        })
    })

    const onDelete = jest.fn(function (e) {
        this.setData({
            imgUrls: e.detail.currentImgs
        })
    })

    const onHide = jest.fn(function () {
        this.setData({
            show: false
        })
    })

    const id = simulate.load({
        template: `
            <mp-gallery
                id="gallery"
                show="{{show}}"
                bindchange="onChange"
                binddelete="onDelete"
                bindhide="onHide"
                img-urls="{{imgUrls}}"
                delete
                hide-on-click="{{true}}"
                current="{{current}}"
            ></mp-gallery>
        `,
        usingComponents: {
            'mp-gallery': gallery
        },
        data: {
            imgUrls: ['http://qq.com/1.jpg', 'http://qq.com/2.jpg', 'http://qq.com/3.jpg'],
            current: 0,
            show: false
        },
        methods: {
            onChange,
            onDelete,
            onHide
        }
    })

    test('basic', async () => {
        const wrapper = simulate.render(id)
        wrapper.attach(document.createElement('parent-wrapper'))
        const comp = wrapper.querySelector('#gallery')
        await simulate.sleep(0)
        expect(comp.toJSON()).toMatchSnapshot()

        wrapper.setData({ show: true })
        await simulate.sleep(0)
        expect(comp.toJSON()).toMatchSnapshot()

        wrapper.setData({ current: 1 })
        await simulate.sleep(0)
        expect(comp.toJSON()).toMatchSnapshot()

        const deleteBtn = comp.querySelector('.weui-gallery__del')
        deleteBtn.dispatchEvent('tap')
        await simulate.sleep(0)
        expect(comp.toJSON()).toMatchSnapshot()
        expect(onDelete).toBeCalledTimes(1)

        const imgWrap = comp.querySelector('.weui-gallery__img__wrp')
        imgWrap.dispatchEvent('tap')
        await simulate.sleep(0)
        expect(comp.toJSON()).toMatchSnapshot()
        expect(onDelete).toBeCalledTimes(1)
    })
})


================================================
FILE: src/components/gallery/gallery.json
================================================
{
    "component": true,
    "styleIsolation": "apply-shared",
    "usingComponents": {}
}

================================================
FILE: src/components/gallery/gallery.less
================================================
.weui-gallery__opr {
  padding-bottom: 0;
}

.weui-gallery__del {
  padding-bottom: calc(16px + env(safe-area-inset-bottom));
}

.weui-gallery:focus {
  outline: none;
}

.weui-gallery__info {
  padding-top: env(safe-area-inset-top);
}

.weui-gallery__del {
  height: 60px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.weui-icon-delete.weui-icon_gallery-delete {
  background-color: var(--weui-WHITE); /* skyline 不支持 currentColor */
}

================================================
FILE: src/components/gallery/gallery.ts
================================================
Component({
    properties: {
        imgUrls: {
            type: Array,
            value: [],
            observer(newVal) {
                this.setData({ currentImgs: newVal })
            }
        },
        showDelete: {
            // 是否显示delete按钮
            type: Boolean,
            value: true
        },
        show: {
            type: Boolean,
            value: true,
            observer: '_showChange'
        },
        current: {
            type: Number,
            value: 0
        },
        hideOnClick: {
            type: Boolean,
            value: true
        },
        extClass: {
            type: String,
            value: ''
        },
        rootPortal: {
            // 是否使用 root-portal
            type: Boolean,
            value: false
        }
    },
    data: {
        wrapperShow: false,
        innerShow: false,
        currentImgs: []
    },
    ready() {
        this._showChange(this.data.show)
        const data: any = this.data
        this.setData({ currentImgs: data.imgUrls })
    },
    methods: {
        _showChange(show) {
            if (show) {
                this.setData({
                    wrapperShow: true,
                    innerShow: true
                })
            } else {
                this.setData({ innerShow: false })
                setTimeout(() => {
                    this.setData({ wrapperShow: false })
                }, 300)
            }
        },
        change(e) {
            this.setData({
                current: e.detail.current
            })
            this.triggerEvent('change', { current: e.detail.current }, {})
        },
        deleteImg() {
            const data: any = this.data
            const imgs = data.currentImgs
            const url = imgs.splice(data.current, 1)
            this.triggerEvent('delete', { url: url[0], index: data.current }, {})
            if (imgs.length === 0) {
                // @ts-ignore
                this.hideGallery()
                return
            }
            this.setData({
                current: 0,
                currentImgs: imgs
            })
        },
        hideGallery() {
            const data: any = this.data
            if (data.hideOnClick) {
                this.setData({
                    show: false
                })
                this.triggerEvent('hide', {}, {})
            }
        }
    }
})


================================================
FILE: src/components/gallery/gallery.wxml
================================================
<template name="body">
  <view
    class="weui-gallery {{innerShow ? 'weui-animate-fade-in' : 'weui-animate-fade-out'}} {{extClass}}"
    aria-modal="true"
    tabindex="0"
  >
    <view class="weui-gallery__info">{{current+1}}/{{currentImgs.length}}</view>
    <swiper
      class="weui-gallery__img__wrp"
      bindtap="hideGallery"
      indicator-dots="{{false}}"
      bindchange="change"
      current="{{current}}"
      autoplay="{{false}}"
      duration="{{500}}"
    >
      <block wx:for="{{currentImgs}}" wx:key="index">
        <swiper-item>
          <image mode="aspectFit" class="weui-gallery__img" src="{{item}}"></image>
        </swiper-item>
      </block>
    </swiper>
    <view class="weui-gallery__opr" wx:if="{{showDelete}}">
      <view
        aria-role="button"
        aria-label="删除"
        class="weui-gallery__del"
        bindtap="deleteImg"
        hover-class="weui-active"
      >
          <view class="weui-icon-delete weui-icon_gallery-delete"></view>
      </view>
    </view>
  </view>
</template>

<root-portal enable="{{true}}" wx:if="{{rootPortal && wrapperShow}}">
  <view class="root-portal-box">
    <template is="body" data="{{showDelete, extClass, current, innerShow, currentImgs}}" />
  </view>
</root-portal>
<view wx:elif="{{!rootPortal && wrapperShow}}">
  <template is="body" data="{{showDelete, extClass, current, innerShow, currentImgs}}" />
</view>


================================================
FILE: src/components/grids/__test__/__snapshots__/grid.test.ts.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`grids basic 1`] = `
<main>
  <mp-grids>
    <wx-view
      class="weui-grids "
    >
      <wx-view
        appId=""
        class="weui-grid"
        data-url="/0"
        extraData=""
        hoverClass="navigator-hover"
        hoverStartTime="{{50}}"
        hoverStayTime="{{600}}"
        hoverStopPropagation="{{false}}"
        openType="navigate"
        path=""
        target="self"
        url="/0"
        version="release"
        bind:complete=""
        bind:fail=""
        bind:success=""
        bind:tap="openPage"
      >
        <wx-view
          class="weui-grid__icon"
        >
          <wx-image
            alt="{{true}}"
            class="weui-grid__icon_img"
            src="/img-0.png"
          />
        </wx-view>
        <wx-view
          class="weui-grid__label"
        >
          Grid0
        </wx-view>
      </wx-view>
      <wx-view
        appId=""
        class="weui-grid"
        data-url="/1"
        extraData=""
        hoverClass="navigator-hover"
        hoverStartTime="{{50}}"
        hoverStayTime="{{600}}"
        hoverStopPropagation="{{false}}"
        openType="navigate"
        path=""
        target="self"
        url="/1"
        version="release"
        bind:complete=""
        bind:fail=""
        bind:success=""
        bind:tap="openPage"
      >
        <wx-view
          class="weui-grid__icon"
        >
          <wx-image
            alt="{{true}}"
            class="weui-grid__icon_img"
            src="/img-1.png"
          />
        </wx-view>
        <wx-view
          class="weui-grid__label"
        >
          Grid1
        </wx-view>
      </wx-view>
      <wx-view
        appId=""
        class="weui-grid"
        data-url="/2"
        extraData=""
        hoverClass="navigator-hover"
        hoverStartTime="{{50}}"
        hoverStayTime="{{600}}"
        hoverStopPropagation="{{false}}"
        openType="navigate"
        path=""
        target="self"
        url="/2"
        version="release"
        bind:complete=""
        bind:fail=""
        bind:success=""
        bind:tap="openPage"
      >
        <wx-view
          class="weui-grid__icon"
        >
          <wx-image
            alt="{{true}}"
            class="weui-grid__icon_img"
            src="/img-2.png"
          />
        </wx-view>
        <wx-view
          class="weui-grid__label"
        >
          Grid2
        </wx-view>
      </wx-view>
      <wx-view
        appId=""
        class="weui-grid"
        data-url="/3"
        extraData=""
        hoverClass="navigator-hover"
        hoverStartTime="{{50}}"
        hoverStayTime="{{600}}"
        hoverStopPropagation="{{false}}"
        openType="navigate"
        path=""
        target="self"
        url="/3"
        version="release"
        bind:complete=""
        bind:fail=""
        bind:success=""
        bind:tap="openPage"
      >
        <wx-view
          class="weui-grid__icon"
        >
          <wx-image
            alt="{{true}}"
            class="weui-grid__icon_img"
            src="/img-3.png"
          />
        </wx-view>
        <wx-view
          class="weui-grid__label"
        >
          Grid3
        </wx-view>
      </wx-view>
      <wx-view
        appId=""
        class="weui-grid"
        data-url="/4"
        extraData=""
        hoverClass="navigator-hover"
        hoverStartTime="{{50}}"
        hoverStayTime="{{600}}"
        hoverStopPropagation="{{false}}"
        openType="navigate"
        path=""
        target="self"
        url="/4"
        version="release"
        bind:complete=""
        bind:fail=""
        bind:success=""
        bind:tap="openPage"
      >
        <wx-view
          class="weui-grid__icon"
        >
          <wx-image
            alt="{{true}}"
            class="weui-grid__icon_img"
            src="/img-4.png"
          />
        </wx-view>
        <wx-view
          class="weui-grid__label"
        >
          Grid4
        </wx-view>
      </wx-view>
      <wx-view
        appId=""
        class="weui-grid"
        data-url="/5"
        extraData=""
        hoverClass="navigator-hover"
        hoverStartTime="{{50}}"
        hoverStayTime="{{600}}"
        hoverStopPropagation="{{false}}"
        openType="navigate"
        path=""
        target="self"
        url="/5"
        version="release"
        bind:complete=""
        bind:fail=""
        bind:success=""
        bind:tap="openPage"
      >
        <wx-view
          class="weui-grid__icon"
        >
          <wx-image
            alt="{{true}}"
            class="weui-grid__icon_img"
            src="/img-5.png"
          />
        </wx-view>
        <wx-view
          class="weui-grid__label"
        >
          Grid5
        </wx-view>
      </wx-view>
      <wx-view
        appId=""
        class="weui-grid"
        data-url="/6"
        extraData=""
        hoverClass="navigator-hover"
        hoverStartTime="{{50}}"
        hoverStayTime="{{600}}"
        hoverStopPropagation="{{false}}"
        openType="navigate"
        path=""
        target="self"
        url="/6"
        version="release"
        bind:complete=""
        bind:fail=""
        bind:success=""
        bind:tap="openPage"
      >
        <wx-view
          class="weui-grid__icon"
        >
          <wx-image
            alt="{{true}}"
            class="weui-grid__icon_img"
            src="/img-6.png"
          />
        </wx-view>
        <wx-view
          class="weui-grid__label"
        >
          Grid6
        </wx-view>
      </wx-view>
      <wx-view
        appId=""
        class="weui-grid"
        data-url="/7"
        extraData=""
        hoverClass="navigator-hover"
        hoverStartTime="{{50}}"
        hoverStayTime="{{600}}"
        hoverStopPropagation="{{false}}"
        openType="navigate"
        path=""
        target="self"
        url="/7"
        version="release"
        bind:complete=""
        bind:fail=""
        bind:success=""
        bind:tap="openPage"
      >
        <wx-view
          class="weui-grid__icon"
        >
          <wx-image
            alt="{{true}}"
            class="weui-grid__icon_img"
            src="/img-7.png"
          />
        </wx-view>
        <wx-view
          class="weui-grid__label"
        >
          Grid7
        </wx-view>
      </wx-view>
      <wx-view
        appId=""
        class="weui-grid"
        data-url="/8"
        extraData=""
        hoverClass="navigator-hover"
        hoverStartTime="{{50}}"
        hoverStayTime="{{600}}"
        hoverStopPropagation="{{false}}"
        openType="navigate"
        path=""
        target="self"
        url="/8"
        version="release"
        bind:complete=""
        bind:fail=""
        bind:success=""
        bind:tap="openPage"
      >
        <wx-view
          class="weui-grid__icon"
        >
          <wx-image
            alt="{{true}}"
            class="weui-grid__icon_img"
            src="/img-8.png"
          />
        </wx-view>
        <wx-view
          class="weui-grid__label"
        >
          Grid8
        </wx-view>
      </wx-view>
    </wx-view>
  </mp-grids>
</main>
`;


================================================
FILE: src/components/grids/__test__/grid.test.ts
================================================
import path from 'path'
import simulate from 'miniprogram-simulate'

describe('grids', () => {
    const grids = simulate.load(path.resolve(__dirname, '../grids'), 'mp-grids')

    const id = simulate.load({
        template: `
            <mp-grids grids="{{grids}}"></mp-grids>
        `,
        usingComponents: {
            'mp-grids': grids
        },
        data: {
            grids: Array(9)
                .fill(null)
                .map((_, i) => ({
                    imgUrl: `/img-${i}.png`,
                    url: `/${i}`,
                    text: `Grid${i}`
                }))
        }
    })

    test('basic', async () => {
        const comp = simulate.render(id)
        comp.attach(document.createElement('parent-wrapper'))
        await simulate.sleep(0)
        expect(comp.toJSON()).toMatchSnapshot()
    })
})


================================================
FILE: src/components/grids/grids.json
================================================
{
  "component": true,
  "styleIsolation": "apply-shared",
  "usingComponents": {}
}

================================================
FILE: src/components/grids/grids.less
================================================
.weui-grid .weui-grid__icon_img {
  width: 100%;
  height: 100%;
}

/** skyline 不支持 float: left */
.weui-grids {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
}

/** skyline 不支持 float: left */
.weui-grid {
    float: none;
}


================================================
FILE: src/components/grids/grids.ts
================================================
const defaultGridProps = {
    target: 'self',
    url: '',
    openType: 'navigate',
    delta: 1,
    appId: '',
    path: '',
    extraData: '',
    version: 'release',
    hoverClass: 'navigator-hover',
    hoverStopPropagation: false,
    hoverStartTime: 50,
    hoverStayTime: 600,
    bindsuccess: () => {},
    bindfail: () => {},
    bindcomplete: () => {}
}

Component({
    options: {
        pureDataPattern: /^_/
    },
    properties: {
        extClass: {
            type: String,
            value: ''
        },
        grids: {
            type: Array,
            value: [],
            observer: '_onGridsChange'
        }
    },
    data: {
        innerGrids: []
    },
    ready() {},
    methods: {
        _onGridsChange(grids): void {
            if (grids) {
                this.setData({
                    innerGrids: grids.map((grid) => Object.assign({}, defaultGridProps, grid))
                })
            }
        },
        // navigator 改成 view,兼容
        openPage(e) {
            const url = e.currentTarget.dataset.url
            wx.navigateTo({
                url: url,
                complete(res) {
                    
                }
            })
        }
    }
})


================================================
FILE: src/components/grids/grids.wxml
================================================
<view class="weui-grids {{extClass}}">
  <block wx:for="{{innerGrids}}" wx:key="index">
    <!-- 把 navigator 改成 view,因为这里 navigator 现在的实现是 text,和 webview 在布局上有点不同 -->
    <view
      class="weui-grid"
      target="{{item.target}}"
      url="{{item.url}}"
      data-url="{{item.url}}"
      open-type="{{item.openType}}"
      app-id="{{item.appId}}"
      path="{{item.path}}"
      extra-data="{{item.extraData}}"
      version="{{item.version}}"
      hover-class="{{item.hoverClass}}"
      hover-stop-propagation="{{item.hoverStopPropagation}}"
      hover-start-time="{{item.hoverStartTime}}"
      hover-stay-time="{{item.hoverStayTime}}"
      bindsuccess="{{item.bindsuccess}}"
      bindfail="{{item.bindfail}}"
      bindcomplete="{{item.bindcomplete}}"
      bindtap="openPage"
    >
      <view class="weui-grid__icon">
        <image class="weui-grid__icon_img" src="{{item.imgUrl}}" alt></image>
      </view>
      <view class="weui-grid__label">{{item.text}}</view>
    </view>
  </block>
</view>

================================================
FILE: src/compon
Download .txt
gitextract_c6xlk5rv/

├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .github/
│   └── ISSUE_TEMPLATE/
│       ├── ----.md
│       └── --bug.md
├── .gitignore
├── .gitmodules
├── .npmignore
├── .npmrc
├── .prettierrc.js
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── babel.config.js
├── docs/
│   ├── README.md
│   ├── actionsheet.md
│   ├── badge.md
│   ├── cell.md
│   ├── cells.md
│   ├── checkbox-group.md
│   ├── dialog.md
│   ├── form-page.md
│   ├── form.md
│   ├── gallery.md
│   ├── half-screen-dialog.md
│   ├── icon.md
│   ├── loading.md
│   ├── msg.md
│   ├── navigation.md
│   ├── other.md
│   ├── quickstart.md
│   ├── search.md
│   ├── slideview.md
│   ├── tabbar.md
│   ├── toptips.md
│   └── uploader.md
├── gulpfile.js
├── jest.config.js
├── mpflow.config.js
├── package.json
├── src/
│   ├── app.js
│   ├── app.json
│   ├── app.less
│   ├── base/
│   │   ├── CustomPage.js
│   │   └── behaviors/
│   │       └── theme.js
│   ├── components/
│   │   ├── actionsheet/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── actionsheet.test.ts.snap
│   │   │   │   ├── actionsheet.test.ts
│   │   │   │   ├── index.json
│   │   │   │   ├── index.ts
│   │   │   │   └── index.wxml
│   │   │   ├── actionsheet.json
│   │   │   ├── actionsheet.less
│   │   │   ├── actionsheet.ts
│   │   │   └── actionsheet.wxml
│   │   ├── badge/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── badage.test.js.snap
│   │   │   │   └── badage.test.js
│   │   │   ├── badge.json
│   │   │   ├── badge.less
│   │   │   ├── badge.ts
│   │   │   └── badge.wxml
│   │   ├── cell/
│   │   │   ├── cell.json
│   │   │   ├── cell.less
│   │   │   ├── cell.ts
│   │   │   └── cell.wxml
│   │   ├── cells/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── cells.test.js.snap
│   │   │   │   ├── cells.test.js
│   │   │   │   ├── index.js
│   │   │   │   ├── index.json
│   │   │   │   └── index.wxml
│   │   │   ├── cells.json
│   │   │   ├── cells.less
│   │   │   ├── cells.ts
│   │   │   └── cells.wxml
│   │   ├── checkbox/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── checkbox.test.js.snap
│   │   │   │   ├── checkbox.test.js
│   │   │   │   ├── index.js
│   │   │   │   ├── index.json
│   │   │   │   └── index.wxml
│   │   │   ├── checkbox.json
│   │   │   ├── checkbox.less
│   │   │   ├── checkbox.ts
│   │   │   └── checkbox.wxml
│   │   ├── checkbox-group/
│   │   │   ├── checkbox-group.json
│   │   │   ├── checkbox-group.less
│   │   │   ├── checkbox-group.ts
│   │   │   └── checkbox-group.wxml
│   │   ├── dialog/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── dialog.test.js.snap
│   │   │   │   ├── dialog.test.js
│   │   │   │   ├── index.js
│   │   │   │   ├── index.json
│   │   │   │   └── index.wxml
│   │   │   ├── dialog.json
│   │   │   ├── dialog.less
│   │   │   ├── dialog.ts
│   │   │   └── dialog.wxml
│   │   ├── form/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── form.test.ts.snap
│   │   │   │   ├── comp/
│   │   │   │   │   ├── index.json
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── index.wxml
│   │   │   │   ├── form-validator.test.ts
│   │   │   │   ├── form.test.ts
│   │   │   │   └── validator.test.ts
│   │   │   ├── form-validator.ts
│   │   │   ├── form.json
│   │   │   ├── form.ts
│   │   │   ├── form.wxml
│   │   │   └── validator.ts
│   │   ├── form-page/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── form-page.test.ts.snap
│   │   │   │   ├── form-page.test.ts
│   │   │   │   ├── index.js
│   │   │   │   ├── index.json
│   │   │   │   └── index.wxml
│   │   │   ├── form-page.json
│   │   │   ├── form-page.less
│   │   │   ├── form-page.ts
│   │   │   └── form-page.wxml
│   │   ├── gallery/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── gallery.test.ts.snap
│   │   │   │   └── gallery.test.ts
│   │   │   ├── gallery.json
│   │   │   ├── gallery.less
│   │   │   ├── gallery.ts
│   │   │   └── gallery.wxml
│   │   ├── grids/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── grid.test.ts.snap
│   │   │   │   └── grid.test.ts
│   │   │   ├── grids.json
│   │   │   ├── grids.less
│   │   │   ├── grids.ts
│   │   │   └── grids.wxml
│   │   ├── half-screen-dialog/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── half-screen-dialog.test.ts.snap
│   │   │   │   └── half-screen-dialog.test.ts
│   │   │   ├── half-screen-dialog.json
│   │   │   ├── half-screen-dialog.less
│   │   │   ├── half-screen-dialog.ts
│   │   │   └── half-screen-dialog.wxml
│   │   ├── icon/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── icon.test.ts.snap
│   │   │   │   └── icon.test.ts
│   │   │   ├── base64.ts
│   │   │   ├── icon.json
│   │   │   ├── icon.less
│   │   │   ├── icon.ts
│   │   │   ├── icon.wxml
│   │   │   └── icondata.ts
│   │   ├── index.json
│   │   ├── index.less
│   │   ├── index.ts
│   │   ├── index.wxml
│   │   ├── loading/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── loading.test.ts.snap
│   │   │   │   └── loading.test.ts
│   │   │   ├── loading.json
│   │   │   ├── loading.less
│   │   │   ├── loading.ts
│   │   │   └── loading.wxml
│   │   ├── msg/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── msg.test.ts.snap
│   │   │   │   └── msg.test.ts
│   │   │   ├── msg.json
│   │   │   ├── msg.less
│   │   │   ├── msg.ts
│   │   │   └── msg.wxml
│   │   ├── navigation-bar/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── navigation-bar.test.ts.snap
│   │   │   │   └── navigation-bar.test.ts
│   │   │   ├── navigation-bar.json
│   │   │   ├── navigation-bar.less
│   │   │   ├── navigation-bar.ts
│   │   │   └── navigation-bar.wxml
│   │   ├── patch.less
│   │   ├── searchbar/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── searchbar.test.ts.snap
│   │   │   │   └── searchbar.test.ts
│   │   │   ├── searchbar.json
│   │   │   ├── searchbar.less
│   │   │   ├── searchbar.ts
│   │   │   └── searchbar.wxml
│   │   ├── slideview/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── slideview.test.ts.snap
│   │   │   │   └── slideview.test.ts
│   │   │   ├── slideview-skyline.json
│   │   │   ├── slideview-skyline.less
│   │   │   ├── slideview-skyline.ts
│   │   │   ├── slideview-skyline.wxml
│   │   │   ├── slideview.json
│   │   │   ├── slideview.less
│   │   │   ├── slideview.ts
│   │   │   ├── slideview.wxml
│   │   │   └── slideview.wxs
│   │   ├── tabbar/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── tabbar.test.ts.snap
│   │   │   │   └── tabbar.test.ts
│   │   │   ├── tabbar.json
│   │   │   ├── tabbar.less
│   │   │   ├── tabbar.ts
│   │   │   └── tabbar.wxml
│   │   ├── toptips/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── toptips.test.ts.snap
│   │   │   │   └── toptips.test.ts
│   │   │   ├── toptips.json
│   │   │   ├── toptips.less
│   │   │   ├── toptips.ts
│   │   │   └── toptips.wxml
│   │   ├── uploader/
│   │   │   ├── __test__/
│   │   │   │   ├── __snapshots__/
│   │   │   │   │   └── uploader.test.ts.snap
│   │   │   │   └── uploader.test.ts
│   │   │   ├── uploader.json
│   │   │   ├── uploader.less
│   │   │   ├── uploader.ts
│   │   │   └── uploader.wxml
│   │   └── utils/
│   │       ├── object.ts
│   │       ├── string.ts
│   │       └── utils.less
│   ├── example/
│   │   ├── actionsheet/
│   │   │   ├── actionsheet.js
│   │   │   ├── actionsheet.json
│   │   │   ├── actionsheet.wxml
│   │   │   └── actionsheet.wxss
│   │   ├── article/
│   │   │   ├── article.js
│   │   │   ├── article.json
│   │   │   ├── article.wxml
│   │   │   └── article.wxss
│   │   ├── badge/
│   │   │   ├── badge.js
│   │   │   ├── badge.json
│   │   │   ├── badge.wxml
│   │   │   └── badge.wxss
│   │   ├── button/
│   │   │   ├── button.js
│   │   │   ├── button.json
│   │   │   ├── button.wxml
│   │   │   └── button.wxss
│   │   ├── cell/
│   │   │   ├── cell.js
│   │   │   ├── cell.json
│   │   │   ├── cell.wxml
│   │   │   └── cell.wxss
│   │   ├── common.less
│   │   ├── dialog/
│   │   │   ├── dialog.js
│   │   │   ├── dialog.json
│   │   │   ├── dialog.wxml
│   │   │   └── dialog.wxss
│   │   ├── flex/
│   │   │   ├── flex.js
│   │   │   ├── flex.json
│   │   │   ├── flex.wxml
│   │   │   └── flex.wxss
│   │   ├── footer/
│   │   │   ├── footer.js
│   │   │   ├── footer.json
│   │   │   ├── footer.wxml
│   │   │   └── footer.wxss
│   │   ├── form/
│   │   │   ├── form.js
│   │   │   ├── form.json
│   │   │   ├── form.wxml
│   │   │   └── form.wxss
│   │   ├── form-page/
│   │   │   ├── form-page.js
│   │   │   ├── form-page.json
│   │   │   ├── form-page.wxml
│   │   │   └── form-page.wxss
│   │   ├── gallery/
│   │   │   ├── gallery.js
│   │   │   ├── gallery.json
│   │   │   ├── gallery.wxml
│   │   │   └── gallery.wxss
│   │   ├── grid/
│   │   │   ├── grid.js
│   │   │   ├── grid.json
│   │   │   ├── grid.wxml
│   │   │   └── grid.wxss
│   │   ├── half-screen-dialog/
│   │   │   ├── half-screen-dialog.js
│   │   │   ├── half-screen-dialog.json
│   │   │   ├── half-screen-dialog.wxml
│   │   │   └── half-screen-dialog.wxss
│   │   ├── icons/
│   │   │   ├── icons.js
│   │   │   ├── icons.json
│   │   │   ├── icons.wxml
│   │   │   └── icons.wxss
│   │   ├── images/
│   │   │   └── base64.js
│   │   ├── index.js
│   │   ├── index.json
│   │   ├── index.less
│   │   ├── index.wxml
│   │   ├── loading/
│   │   │   ├── loading.js
│   │   │   ├── loading.json
│   │   │   ├── loading.wxml
│   │   │   └── loading.wxss
│   │   ├── loadmore/
│   │   │   ├── loadmore.js
│   │   │   ├── loadmore.json
│   │   │   ├── loadmore.wxml
│   │   │   └── loadmore.wxss
│   │   ├── msg/
│   │   │   ├── msg.js
│   │   │   ├── msg.json
│   │   │   ├── msg.wxml
│   │   │   ├── msg.wxss
│   │   │   ├── msg_fail.js
│   │   │   ├── msg_fail.json
│   │   │   ├── msg_fail.wxml
│   │   │   ├── msg_fail.wxss
│   │   │   ├── msg_success.js
│   │   │   ├── msg_success.json
│   │   │   ├── msg_success.wxml
│   │   │   ├── msg_success.wxss
│   │   │   ├── msg_text.js
│   │   │   ├── msg_text.json
│   │   │   ├── msg_text.wxml
│   │   │   ├── msg_text.wxss
│   │   │   ├── msg_text_primary.js
│   │   │   ├── msg_text_primary.json
│   │   │   ├── msg_text_primary.wxml
│   │   │   └── msg_text_primary.wxss
│   │   ├── navbar/
│   │   │   ├── navbar.js
│   │   │   ├── navbar.json
│   │   │   ├── navbar.wxml
│   │   │   └── navbar.wxss
│   │   ├── navigation/
│   │   │   ├── navigation.js
│   │   │   ├── navigation.json
│   │   │   ├── navigation.wxml
│   │   │   └── navigation.wxss
│   │   ├── panel/
│   │   │   ├── panel.js
│   │   │   ├── panel.json
│   │   │   ├── panel.wxml
│   │   │   └── panel.wxss
│   │   ├── picker/
│   │   │   ├── picker.js
│   │   │   ├── picker.json
│   │   │   ├── picker.wxml
│   │   │   └── picker.wxss
│   │   ├── preview/
│   │   │   ├── preview.js
│   │   │   ├── preview.json
│   │   │   ├── preview.wxml
│   │   │   └── preview.wxss
│   │   ├── progress/
│   │   │   ├── progress.js
│   │   │   ├── progress.json
│   │   │   ├── progress.wxml
│   │   │   └── progress.wxss
│   │   ├── searchbar/
│   │   │   ├── searchbar.js
│   │   │   ├── searchbar.json
│   │   │   ├── searchbar.wxml
│   │   │   └── searchbar.wxss
│   │   ├── slider/
│   │   │   ├── slider.js
│   │   │   ├── slider.json
│   │   │   ├── slider.wxml
│   │   │   └── slider.wxss
│   │   ├── slideview/
│   │   │   ├── slideview.js
│   │   │   ├── slideview.json
│   │   │   ├── slideview.wxml
│   │   │   └── slideview.wxss
│   │   ├── tabbar/
│   │   │   ├── tabbar.js
│   │   │   ├── tabbar.json
│   │   │   ├── tabbar.wxml
│   │   │   └── tabbar.wxss
│   │   ├── toast/
│   │   │   ├── toast.js
│   │   │   ├── toast.json
│   │   │   ├── toast.wxml
│   │   │   └── toast.wxss
│   │   ├── toptips/
│   │   │   ├── toptips.js
│   │   │   ├── toptips.json
│   │   │   ├── toptips.wxml
│   │   │   └── toptips.wxss
│   │   └── uploader/
│   │       ├── uploader.js
│   │       ├── uploader.json
│   │       ├── uploader.wxml
│   │       └── uploader.wxss
│   └── project.config.json
├── tools/
│   ├── build.js
│   ├── checkcomponents.js
│   ├── checkwxss.js
│   ├── config.js
│   ├── package.json
│   └── utils.js
└── tsconfig.json
Download .txt
SYMBOL INDEX (217 symbols across 52 files)

FILE: src/app.js
  method themeChanged (line 16) | themeChanged(theme) {
  method watchThemeChange (line 22) | watchThemeChange(listener) {
  method unWatchThemeChange (line 27) | unWatchThemeChange(listener) {

FILE: src/base/CustomPage.js
  method onLoad (line 7) | onLoad(query) {
  method onUnload (line 13) | onUnload() {

FILE: src/base/behaviors/theme.js
  method themeChanged (line 6) | themeChanged(theme) {

FILE: src/components/actionsheet/__test__/index.ts
  method actiontap (line 18) | actiontap(event): void {
  method close (line 23) | close(): void {

FILE: src/components/actionsheet/actionsheet.ts
  method ready (line 66) | ready() {
  method _showChange (line 72) | _showChange(show) {
  method _groupChange (line 87) | _groupChange(e): void {
  method buttonTap (line 95) | buttonTap(e): void {
  method closeActionSheet (line 99) | closeActionSheet(e): void {

FILE: src/components/cell/cell.ts
  method setError (line 100) | setError(error) {
  method setInForm (line 105) | setInForm() {
  method setOuterClass (line 110) | setOuterClass(className) {
  method navigateTo (line 115) | navigateTo() {

FILE: src/components/cells/__test__/index.js
  method actiontap (line 11) | actiontap(event) {
  method close (line 16) | close() {

FILE: src/components/cells/cells.ts
  method linked (line 35) | linked(target) {
  method linked (line 50) | linked(target) {
  method unlinked (line 56) | unlinked(target) {
  method setCellMulti (line 65) | setCellMulti(multi) {
  method setCellsClass (line 70) | setCellsClass(className) {
  method setOuterClass (line 75) | setOuterClass(className) {

FILE: src/components/checkbox-group/checkbox-group.ts
  method linked (line 29) | linked(target) {
  method unlinked (line 40) | unlinked(target) {
  method linked (line 59) | linked(target) {
  method unlinked (line 65) | unlinked() {
  method checkedChange (line 71) | checkedChange(checked, target) {
  method setParentCellsClass (line 96) | setParentCellsClass() {
  method _multiChange (line 102) | _multiChange(multi) {

FILE: src/components/checkbox/__test__/index.js
  method radioChange (line 15) | radioChange(e) {
  method checkboxChange (line 20) | checkboxChange(e) {

FILE: src/components/checkbox/checkbox.ts
  method linked (line 35) | linked(target) {
  method unlinked (line 38) | unlinked() {
  method setMulti (line 44) | setMulti(multi) {
  method setOuterClass (line 49) | setOuterClass(className) {
  method checkedChange (line 54) | checkedChange() {

FILE: src/components/dialog/__test__/index.js
  method buttontap (line 8) | buttontap(e) {
  method close (line 13) | close(e) {

FILE: src/components/dialog/dialog.ts
  method ready (line 46) | ready() {
  method _showChange (line 64) | _showChange(show) {
  method buttonTap (line 77) | buttonTap(e) {
  method close (line 81) | close() {
  method stopEvent (line 89) | stopEvent() {}

FILE: src/components/form-page/form-page.ts
  method linked (line 20) | linked(target) {

FILE: src/components/form/__test__/comp/index.ts
  method formInputChange (line 114) | formInputChange(e) {
  method submitForm (line 151) | submitForm() {

FILE: src/components/form/form-validator.ts
  class FormValidator (line 23) | class FormValidator {
    method constructor (line 30) | constructor(models, rules) {
    method validate (line 38) | validate(cb?: any) {
    method validateField (line 66) | validateField(name, value, cb?) {
    method _innerValidateField (line 83) | _innerValidateField(name, value, cb) {
    method addMethod (line 130) | static addMethod(ruleName, method) {
    method setModel (line 134) | setModel(newModel) {
    method setRules (line 138) | setRules(newRules) {

FILE: src/components/form/form.ts
  function linked (line 4) | function linked(target) {
  function unlinked (line 15) | function unlinked(target) {
  method attached (line 56) | attached() {
  method initRules (line 61) | initRules(rules) {
  method _modelChange (line 71) | _modelChange(newVal, oldVal) {
  method _rulesChange (line 99) | _rulesChange(newVal) {
  method _showAllErrors (line 106) | _showAllErrors(errors) {
  method _showErrors (line 111) | _showErrors(objs, errors) {
  method _showError (line 116) | _showError(prop, error) {
  method validate (line 122) | validate(cb) {
  method validateField (line 134) | validateField(name, value, cb = (v, error = null) => {}) {
  method handleErrors (line 145) | handleErrors(errors) {
  method addMethod (line 158) | addMethod(ruleName, method) {

FILE: src/components/form/validator.ts
  method required (line 24) | required(r, val): string {
  method minlength (line 29) | minlength(r, val) {
  method maxlength (line 35) | maxlength(r, val) {
  method rangelength (line 44) | rangelength(r, val) {
  method min (line 53) | min(r, val) {
  method max (line 61) | max(r, val) {
  method range (line 69) | range(r, val) {
  method mobile (line 77) | mobile(r, val) {
  method email (line 87) | email(r, value) {
  method url (line 99) | url(r, value) {
  method equalTo (line 113) | equalTo(r, value, param, models) {
  method bytelength (line 121) | bytelength(r, value, param) {

FILE: src/components/gallery/gallery.ts
  method observer (line 6) | observer(newVal) {
  method ready (line 43) | ready() {
  method _showChange (line 49) | _showChange(show) {
  method change (line 62) | change(e) {
  method deleteImg (line 68) | deleteImg() {
  method hideGallery (line 83) | hideGallery() {

FILE: src/components/grids/grids.ts
  method ready (line 37) | ready() {}
  method _onGridsChange (line 39) | _onGridsChange(grids): void {
  method openPage (line 47) | openPage(e) {

FILE: src/components/half-screen-dialog/half-screen-dialog.ts
  method ready (line 64) | ready() {
  method _showChange (line 69) | _showChange(show) {
  method close (line 82) | close(e) {
  method buttonTap (line 92) | buttonTap(e) {

FILE: src/components/icon/__test__/icon.test.ts
  function testIcon (line 7) | async function testIcon(options: {

FILE: src/components/icon/icon.ts
  method _genSrcByIcon (line 40) | _genSrcByIcon(v) {
  method _genSrcByType (line 43) | _genSrcByType(v) {
  method _genSrc (line 47) | _genSrc(rawData) {

FILE: src/components/navigation-bar/navigation-bar.ts
  method attached (line 56) | attached() {
  method _showChange (line 73) | _showChange(show) {
  method back (line 87) | back() {

FILE: src/components/searchbar/__test__/searchbar.test.ts
  method ready (line 17) | ready() {
  method search (line 23) | search(value: string) {

FILE: src/components/searchbar/searchbar.ts
  method attached (line 47) | attached() {
  method valueChange (line 57) | valueChange() {
  method clearInput (line 64) | clearInput() {
  method inputFocus (line 75) | inputFocus(e) {
  method inputBlur (line 83) | inputBlur(e) {
  method showInput (line 89) | showInput() {
  method hideInput (line 95) | hideInput() {
  method inputChange (line 102) | inputChange(e) {
  method selectResult (line 128) | selectResult(e) {

FILE: src/components/slideview/slideview-skyline.ts
  method observer (line 17) | observer() {
  method observer (line 33) | observer() {
  method ready (line 57) | ready() {
  method addClassNameForButton (line 75) | addClassNameForButton() {
  method buttonTap (line 91) | buttonTap(event) {
  method transitionEnd (line 103) | transitionEnd() {}
  method showButtons (line 105) | showButtons() {
  method hideButtons (line 116) | hideButtons() {
  method initAnimate (line 129) | initAnimate() {
  method touchstart (line 169) | touchstart(event) {
  method touchmove (line 178) | touchmove(event) {
  method touchend (line 201) | touchend(event) {

FILE: src/components/slideview/slideview.ts
  method observer (line 16) | observer() {
  method created (line 55) | created() {
  method ready (line 59) | ready() {
  method updateRight (line 67) | updateRight() {
  method addClassNameForButton (line 93) | addClassNameForButton() {
  method buttonTapByWxs (line 109) | buttonTapByWxs(data) {
  method hide (line 112) | hide() {
  method show (line 115) | show() {
  method transitionEnd (line 118) | transitionEnd() {}

FILE: src/components/tabbar/tabbar.ts
  method tabChange (line 21) | tabChange(e) {

FILE: src/components/toptips/toptips.ts
  method ready (line 46) | ready() {
  method attached (line 49) | attached() {
  method _typeChange (line 54) | _typeChange(type) {
  method _showChange (line 59) | _showChange(show) {
  method _showToptips (line 66) | _showToptips() {
  method _hideToptips (line 86) | _hideToptips() {

FILE: src/components/uploader/__test__/uploader.test.ts
  method ready (line 45) | ready() {
  method uploadFile (line 51) | uploadFile(files) {
  method ready (line 81) | ready() {
  method uploadFile (line 87) | uploadFile(files) {

FILE: src/components/uploader/uploader.ts
  method observer (line 31) | observer(newVal) {
  method ready (line 67) | ready() {}
  method previewImage (line 69) | previewImage(e) {
  method chooseImage (line 81) | chooseImage() {
  method deletePic (line 208) | deletePic(e) {

FILE: src/example/actionsheet/actionsheet.js
  method btnClick (line 32) | btnClick(e) {

FILE: src/example/cell/cell.js
  method slideButtonTap (line 27) | slideButtonTap(e) {

FILE: src/example/dialog/dialog.js
  method tapDialogButton (line 15) | tapDialogButton(e) {
  method tapOneDialogButton (line 21) | tapOneDialogButton(e) {

FILE: src/example/form/form.js
  method formInputChange (line 106) | formInputChange(e) {
  method submitForm (line 143) | submitForm() {

FILE: src/example/gallery/gallery.js
  method openGallery (line 12) | openGallery() {
  method change (line 15) | change(e) {
  method delete (line 18) | delete(e) {
  method hide (line 21) | hide() {

FILE: src/example/half-screen-dialog/half-screen-dialog.js
  method buttontap (line 38) | buttontap(e) {

FILE: src/example/icons/icons.js
  method onLoad (line 497) | onLoad() {
  method setIconColor (line 503) | setIconColor(theme) {

FILE: src/example/index.js
  method themeToggle (line 65) | themeToggle() {
  method openPage (line 77) | openPage(e) {

FILE: src/example/loading/loading.js
  method onShow (line 9) | onShow() {
  method close (line 16) | close() {
  method onUnload (line 21) | onUnload() {

FILE: src/example/navigation/navigation.js
  method toggleLoading (line 10) | toggleLoading() {
  method changeTitle (line 15) | changeTitle() {
  method changeColor (line 20) | changeColor() {
  method changeBgColor (line 25) | changeBgColor() {
  method toggleShow (line 30) | toggleShow() {
  method toggleAnimated (line 35) | toggleAnimated() {

FILE: src/example/progress/progress.js
  function _next (line 1) | function _next(){

FILE: src/example/searchbar/searchbar.js
  method onLoad (line 9) | onLoad() {

FILE: src/example/slideview/slideview.js
  method show (line 27) | show() {
  method hide (line 30) | hide() {
  method slideButtonTap (line 33) | slideButtonTap(e) {

FILE: src/example/tabbar/tabbar.js
  method tabChange (line 34) | tabChange(e) {

FILE: src/example/toptips/toptips.js
  method showToptips1 (line 9) | showToptips1() {
  method showToptips2 (line 14) | showToptips2() {
  method showToptips3 (line 19) | showToptips3() {

FILE: src/example/uploader/uploader.js
  method onLoad (line 9) | onLoad() {
  method selectFile (line 34) | selectFile(files) {
  method uploadFile (line 38) | uploadFile(files) {
  method uploadError (line 47) | uploadError(e) {
  method uploadSuccess (line 50) | uploadSuccess(e) {

FILE: tools/build.js
  function wxss (line 24) | function wxss(wxssFileList) {
  function buildLess (line 44) | function buildLess(lessFileList) {
  function js (line 65) | function js(jsFileMap, scope) {
  function ts (line 109) | function ts(tsFileMap, scope) {
  function copy (line 151) | function copy(copyFileList) {
  function copyWeuiWxss (line 173) | function copyWeuiWxss() {
  function install (line 183) | function install() {
  class BuildTask (line 200) | class BuildTask {
    method constructor (line 201) | constructor(id, entry) {
    method init (line 213) | init() {

FILE: tools/checkcomponents.js
  function getJsonPathInfo (line 11) | function getJsonPathInfo(jsonPath) {
  function checkIncludedComponents (line 26) | async function checkIncludedComponents(jsonPath, componentListMap) {

FILE: tools/checkwxss.js
  function getImportList (line 10) | function getImportList(wxss, filePath) {
  function getContent (line 29) | async function getContent(wxss, filePath, cwd) {
  method start (line 65) | start() {
  method end (line 90) | end() {

FILE: tools/utils.js
  function wrap (line 11) | function wrap(func, scope) {
  function transformPath (line 41) | function transformPath(filePath, sep = '/') {
  function checkFileExists (line 48) | async function checkFileExists(filePath) {
  function recursiveMkdir (line 60) | async function recursiveMkdir(dirPath) {
  function readJson (line 87) | function readJson(filePath) {
  function readFile (line 101) | async function readFile(filePath) {
  function writeFile (line 113) | async function writeFile(filePath, data) {
  function format (line 126) | function format(time, reg) {
  function logger (line 148) | function logger(action = 'copy') {
  function compareArray (line 163) | function compareArray(arr1, arr2) {
  function merge (line 177) | function merge(obj1, obj2) {
  function getId (line 195) | function getId() {
  function dealWithWeuiWxss (line 202) | function dealWithWeuiWxss() {
Condensed preview — 354 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (709K chars).
[
  {
    "path": ".eslintignore",
    "chars": 65,
    "preview": "node_modules\nsrc/weui-wxss\nminiprogram_dist\nminiprogram_dev\ntools"
  },
  {
    "path": ".eslintrc.js",
    "chars": 993,
    "preview": "module.exports = {\n    root: true,\n    parser: '@typescript-eslint/parser',\n    parserOptions: {\n        ecmaVersion: 6,"
  },
  {
    "path": ".gitattributes",
    "chars": 12,
    "preview": "* text=auto\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/----.md",
    "chars": 130,
    "preview": "---\nname: 特性请求\nabout: 提出一个好的特性\ntitle: \"[FEATURE]\"\nlabels: ''\nassignees: ''\n\n---\n\n## 遇到的问题\n描述你想要达到的效果,以及你的使用场景\n\n## 特性方案\n描"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/--bug.md",
    "chars": 275,
    "preview": "---\nname: 报告Bug\nabout: 创建一个Bug报告\ntitle: \"[BUG]\"\nlabels: ''\nassignees: ''\n\n---\n\n## Bug描述\n描述你遇到的Bug,所产生的非预期行为\n\n## 复现方式\n复现该"
  },
  {
    "path": ".gitignore",
    "chars": 136,
    "preview": ".idea\n.DS_Store\n\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\nminiprogram_dist\nminiprogram_dev\nnode_module"
  },
  {
    "path": ".gitmodules",
    "chars": 0,
    "preview": ""
  },
  {
    "path": ".npmignore",
    "chars": 173,
    "preview": ".idea\n.DS_Store\npackage-lock.json\n\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\ntest\ntools\ndocs\nminiprogra"
  },
  {
    "path": ".npmrc",
    "chars": 36,
    "preview": "registry=https://registry.npmjs.org\n"
  },
  {
    "path": ".prettierrc.js",
    "chars": 659,
    "preview": "module.exports = {\n  // 一行最多 100 字符\n  printWidth: 100,\n  // 使用 4 个空格缩进\n  tabWidth: 4,\n  // 不使用缩进符,而使用空格\n  useTabs: false"
  },
  {
    "path": ".travis.yml",
    "chars": 97,
    "preview": "language: node_js\nnode_js:\n  - \"v12.16.3\"\nscript: npm run test\nafter_script:\n  - npm run codecov\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 2605,
    "preview": "# 更新日志\n\n## 1.5.6\n\n- fix: 修改 ext-class 实现\n- fix: 修复 checkbox 样式错误\n\n## 1.5.5\n\n- fix: 修复 cell 丢失边缘线样式\n- fix: 修复 checkbox 样式"
  },
  {
    "path": "LICENSE",
    "chars": 1091,
    "preview": "MIT License\n\nCopyright (c) 2019 cunjinli,rockhou,xushengni,tomylin\n\nPermission is hereby granted, free of charge, to any"
  },
  {
    "path": "README.md",
    "chars": 1177,
    "preview": "## WeUI组件库简介\n\n[![](https://img.shields.io/npm/v/weui-miniprogram.svg?style=flat-square)](https://www.npmjs.com/package/w"
  },
  {
    "path": "babel.config.js",
    "chars": 102,
    "preview": "module.exports = {\n    presets: ['@mpflow/plugin-babel/preset', '@mpflow/plugin-typescript/preset']\n}\n"
  },
  {
    "path": "docs/README.md",
    "chars": 634,
    "preview": "## WeUI组件库简介\n这是一套基于样式库[weui-wxss](https://github.com/Tencent/weui-wxss/)开发的小程序扩展组件库,同微信原生视觉体验一致的UI组件库,由微信官方设计团队和小程序团队为微信"
  },
  {
    "path": "docs/actionsheet.md",
    "chars": 1556,
    "preview": "# ActionSheet\n底部弹起的操作按钮组件\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    \"mp-actionSheet\": \"weui-minipr"
  },
  {
    "path": "docs/badge.md",
    "chars": 330,
    "preview": "# Badge徽章\n出现在按钮、图标附近的数字或者状态标记。\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    \"mp-badge\": \"weui-minipro"
  },
  {
    "path": "docs/cell.md",
    "chars": 1137,
    "preview": "# Cell\nCell是列表或者是表单的一项,常用于设置页的展示,或者用在表单中,作为表单的每一个要填写的项,Cell必须要放在[Cells](./cells.md)组件的下面。\n\n## 代码引入\n在 page.json 中引入组件\n```"
  },
  {
    "path": "docs/cells.md",
    "chars": 867,
    "preview": "# Cells\nCells是列表分组,常用于嵌套一组Cell或者[Checkbox](./checkbox.md),注意,Checkbox-group和Cell组件都必须放在Cells组件下面,Cells效果如下图所示。\n\n![](./im"
  },
  {
    "path": "docs/checkbox-group.md",
    "chars": 3268,
    "preview": "# Checkbox-group和Checkbox\nCheckbox-group是由一组单选或者多选Checkbox组件组成,效果如下图所示。\n\n![](./img/checkbox-group.png#width:300px)\n\n## 引"
  },
  {
    "path": "docs/dialog.md",
    "chars": 871,
    "preview": "# Dialog\nDialog弹窗组件。\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    \"mp-dialog\": \"weui-miniprogram/dial"
  },
  {
    "path": "docs/form-page.md",
    "chars": 1529,
    "preview": "# FormPage\n表单页面,规定了标准表单的顶部的标题和底部的按钮提示等区域的规范\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    \"mp-form-pag"
  },
  {
    "path": "docs/form.md",
    "chars": 2047,
    "preview": "# Form\nForm表单组件,结合Cell、Checkbox-group、Checkbox组件等做表单校验。\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    "
  },
  {
    "path": "docs/gallery.md",
    "chars": 723,
    "preview": "# Gallery画廊\n用于多张图片展示,类似原生的wx.previewImage的展示。\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    \"mp-galler"
  },
  {
    "path": "docs/half-screen-dialog.md",
    "chars": 1853,
    "preview": "# Half Screen Dialog\n半屏弹窗,辅助完成当前页面任务时;提醒用户并引导用户的下一步操作;用户主动发起的任务时。\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponent"
  },
  {
    "path": "docs/icon.md",
    "chars": 690,
    "preview": "# Icon\n图标\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    \"mp-icon\": \"weui-miniprogram/icon/icon\"\n  }\n}\n"
  },
  {
    "path": "docs/loading.md",
    "chars": 508,
    "preview": "# Loading加载\n加载数据时的 loading 效果\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    \"mp-loading\": \"weui-minipr"
  },
  {
    "path": "docs/msg.md",
    "chars": 814,
    "preview": "# Msg\nMsg组件提供操作确认页或操作成功或失败的标准的确认页的样式。\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    \"mp-msg\": \"weui-mi"
  },
  {
    "path": "docs/navigation.md",
    "chars": 1049,
    "preview": "# Navigation\nNavigation是小程序的顶部导航组件,当页面配置`navigationStyle`设置为`custom`的时候可以使用此组件替代原生导航栏。\n\n## 代码引入\n在 page.json 中引入组件\n```jso"
  },
  {
    "path": "docs/other.md",
    "chars": 1820,
    "preview": "# 其他组件\n\n出于性能考虑,`weui-miniprogram` 并没有对所有 WeUI 组件进行封装(如:flex布局组件),可以直接使用 WeUI 中定义的组件结构(参考 [Demo](https://github.com/wecha"
  },
  {
    "path": "docs/quickstart.md",
    "chars": 1718,
    "preview": "# 快速上手\n\n## 使用之前\n\n扩展组件库基于小程序自定义组件构建,在使用扩展组件库之前,建议先阅读熟悉小程序[自定义组件](../../framework/custom-component/index.md)。\n\n## 引入组件\n\n1."
  },
  {
    "path": "docs/search.md",
    "chars": 951,
    "preview": "# Searchbar\n搜索组件Searchbar提供搜索的功能,并展示搜索的结果。\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    \"mp-searchbar"
  },
  {
    "path": "docs/slideview.md",
    "chars": 1169,
    "preview": "# Slideview\n左滑删除组件,基础库 2.4.4 开始支持。\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    \"mp-slideview\": \"weui"
  },
  {
    "path": "docs/tabbar.md",
    "chars": 969,
    "preview": "# Tabbar\nTabbar组件,也可以用来作为小程序的[自定义Tabbar](https://developers.weixin.qq.com/miniprogram/dev/framework/ability/custom-tabba"
  },
  {
    "path": "docs/toptips.md",
    "chars": 864,
    "preview": "# Toptips\nToptips顶部错误提示组件,常用于表单校验或提交请求到后台成功或失败的错误提示,如下图所示。\n\n![](./img/toptips.png#width:300px)\n\n## 引入组件\n在 page.json 中引入组"
  },
  {
    "path": "docs/uploader.md",
    "chars": 1550,
    "preview": "# Uploader\n图片上传Uploader组件。\n\n## 代码引入\n在 page.json 中引入组件\n```json\n{\n  \"usingComponents\": {\n    \"mp-uploader\": \"weui-miniprog"
  },
  {
    "path": "gulpfile.js",
    "chars": 812,
    "preview": "const gulp = require('gulp')\nconst clean = require('gulp-clean')\n\nconst config = require('./tools/config')\nconst BuildTa"
  },
  {
    "path": "jest.config.js",
    "chars": 429,
    "preview": "module.exports = {\n    bail: 1,\n    verbose: true,\n    testEnvironment: 'jsdom',\n    testURL: 'https://jest.test',\n    m"
  },
  {
    "path": "mpflow.config.js",
    "chars": 1568,
    "preview": "const TerserWebpackPlugin = require('terser-webpack-plugin')\n\nmodule.exports = {\n    appId: 'wxe5f52902cf4de896',\n    pr"
  },
  {
    "path": "package.json",
    "chars": 2766,
    "preview": "{\n  \"name\": \"weui-miniprogram\",\n  \"version\": \"1.5.6\",\n  \"description\": \"小程序 WeUI 组件库\",\n  \"main\": \"miniprogram_dist/index"
  },
  {
    "path": "src/app.js",
    "chars": 1006,
    "preview": "const themeListeners = []\n\nApp({\n    onLaunch: function () {\n        console.log('App Launch')\n        wx.onThemeChange("
  },
  {
    "path": "src/app.json",
    "chars": 1639,
    "preview": "{\n  \"pages\": [\n    \"example/index\",\n\n    \"example/button/button\",\n    \"example/cell/cell\",\n    \"example/slideview/slidev"
  },
  {
    "path": "src/app.less",
    "chars": 341,
    "preview": "@import \"./example/common.less\";\n\n.page {\n  display: flex;\n  width: 100vw;\n  height: 100vh;\n  flex-direction: column;\n}\n"
  },
  {
    "path": "src/base/CustomPage.js",
    "chars": 760,
    "preview": "import themeMixin from './behaviors/theme'\n\nconst CustomPage = function (options) {\n    return Page(\n        Object.assi"
  },
  {
    "path": "src/base/behaviors/theme.js",
    "chars": 198,
    "preview": "module.exports = Behavior({\n    data: {\n        theme: 'light'\n    },\n    methods: {\n        themeChanged(theme) {\n     "
  },
  {
    "path": "src/components/actionsheet/__test__/__snapshots__/actionsheet.test.ts.snap",
    "chars": 7484,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`actionsheet basic actionsheet 1`] = `\n<main>\n  <mp-actionsheet\n    "
  },
  {
    "path": "src/components/actionsheet/__test__/actionsheet.test.ts",
    "chars": 2051,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('actionsheet', () => {\n    let id: string\n"
  },
  {
    "path": "src/components/actionsheet/__test__/index.json",
    "chars": 100,
    "preview": "{\n    \"component\": true,\n    \"usingComponents\": {\n        \"mp-actionsheet\": \"../actionsheet\"\n    }\n}"
  },
  {
    "path": "src/components/actionsheet/__test__/index.ts",
    "chars": 607,
    "preview": "Component({\n    properties: {\n        showCancel: {\n            type: Boolean,\n            value: true\n        }\n    },\n"
  },
  {
    "path": "src/components/actionsheet/__test__/index.wxml",
    "chars": 293,
    "preview": "<mp-actionsheet\n    class=\"comp\"\n    title=\"actionsheet title\"\n    show-cancel=\"{{showCancel}}\"\n    cancel-text=\"actions"
  },
  {
    "path": "src/components/actionsheet/actionsheet.json",
    "chars": 84,
    "preview": "{\n  \"component\": true,\n  \"styleIsolation\": \"apply-shared\",\n  \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/actionsheet/actionsheet.less",
    "chars": 1095,
    "preview": ".weui-actionsheet__action {\n  padding-bottom: 0;\n}\n\n.weui-actionsheet__cell_cancel {\n  padding-bottom: calc(16px + env(s"
  },
  {
    "path": "src/components/actionsheet/actionsheet.ts",
    "chars": 2701,
    "preview": "Component({\n    options: {\n        multipleSlots: true // 在组件定义时的选项中启用多slot支持\n    },\n    properties: {\n        title: {\n"
  },
  {
    "path": "src/components/actionsheet/actionsheet.wxml",
    "chars": 2917,
    "preview": "<wxs module=\"utils\">\n    var join = function(a,b) {\n        return a+b\n    };\n    var isNotSlot = function(v) {\n        "
  },
  {
    "path": "src/components/badge/__test__/__snapshots__/badage.test.js.snap",
    "chars": 345,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`badge badge with content 1`] = `\n<main>\n  <wx-view\n    ariaLabel=\"\""
  },
  {
    "path": "src/components/badge/__test__/badage.test.js",
    "chars": 706,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('badge', () => {\n    let id\n\n    beforeAll"
  },
  {
    "path": "src/components/badge/badge.json",
    "chars": 90,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/badge/badge.less",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/components/badge/badge.ts",
    "chars": 275,
    "preview": "Component({\n    properties: {\n        extClass: {\n            type: String,\n            value: ''\n        },\n        con"
  },
  {
    "path": "src/components/badge/badge.wxml",
    "chars": 120,
    "preview": "<view class=\"weui-badge {{extClass}} {{!content ? 'weui-badge_dot' : ''}}\" aria-label=\"{{ariaLabel}}\">{{content}}</view>"
  },
  {
    "path": "src/components/cell/cell.json",
    "chars": 132,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {\n        \"mp-cells\": \"../cells/ce"
  },
  {
    "path": "src/components/cell/cell.less",
    "chars": 175,
    "preview": ".weui-cell_wxss.weui-cell_wxss::before{\n  display: flex;\n}\n\n/* skyline 不支持 currentColor */\n.weui-cell_access .weui-cell_"
  },
  {
    "path": "src/components/cell/cell.ts",
    "chars": 2841,
    "preview": "Component({\n    options: {\n        multipleSlots: true\n    },\n    properties: {\n        hover: {\n            type: Boole"
  },
  {
    "path": "src/components/cell/cell.wxml",
    "chars": 1528,
    "preview": "<view\n    bindtap=\"navigateTo\"\n    class=\"weui-cell {{link ? 'weui-cell_access' : ''}} {{extClass}} {{outerClass}} {{inF"
  },
  {
    "path": "src/components/cells/__test__/__snapshots__/cells.test.js.snap",
    "chars": 1826,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`cells & cell basic cells & cell 1`] = `\n<main>\n  <mp-cells\n    clas"
  },
  {
    "path": "src/components/cells/__test__/cells.test.js",
    "chars": 444,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('cells & cell', () => {\n    let id\n\n    be"
  },
  {
    "path": "src/components/cells/__test__/index.js",
    "chars": 469,
    "preview": "Component({\n    data: {\n        tapValue: 0,\n        actions: [\n            { text: 'item 1', value: 1 },\n            { "
  },
  {
    "path": "src/components/cells/__test__/index.json",
    "chars": 135,
    "preview": "{\n    \"component\": true,\n    \"usingComponents\": {\n        \"mp-cells\": \"../../cells/cells\",\n        \"mp-cell\": \"../../cel"
  },
  {
    "path": "src/components/cells/__test__/index.wxml",
    "chars": 216,
    "preview": "<mp-cells class=\"cells\" title=\"带图标、说明的列表项\" footer=\"底部说明文字\">\n    <mp-cell class=\"cell1\" value=\"标题文字\" footer=\"说明文字\" icon=\""
  },
  {
    "path": "src/components/cells/cells.json",
    "chars": 90,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/cells/cells.less",
    "chars": 201,
    "preview": ".weui-cells__group_wxss.weui-cells__group_wxss .weui-cells__title {\n  margin-top: 24px;\n}\n.weui-cells__group_form .weui-"
  },
  {
    "path": "src/components/cells/cells.ts",
    "chars": 2015,
    "preview": "// import checkboxGroup from \"../checkbox-group/checkbox-group\"\n\nComponent({\n    options: {\n        multipleSlots: true\n"
  },
  {
    "path": "src/components/cells/cells.wxml",
    "chars": 448,
    "preview": "<view class=\"{{extClass}} weui-cells__group {{outerClass}} {{childClass}}\" aria-role=\"{{ariaRole}}\">\n    <view wx:if=\"{{"
  },
  {
    "path": "src/components/checkbox/__test__/__snapshots__/checkbox.test.js.snap",
    "chars": 5811,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`checkbox-group & checkbox basic checkbox 1`] = `\n<main>\n  <mp-cells"
  },
  {
    "path": "src/components/checkbox/__test__/checkbox.test.js",
    "chars": 1759,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('checkbox-group & checkbox', () => {\n    l"
  },
  {
    "path": "src/components/checkbox/__test__/index.js",
    "chars": 659,
    "preview": "Component({\n    data: {\n        radioItems: [\n            { name: 'cell standard', value: '0' },\n            { name: 'ce"
  },
  {
    "path": "src/components/checkbox/__test__/index.json",
    "chars": 174,
    "preview": "{\n    \"component\": true,\n    \"usingComponents\": {\n        \"mp-checkbox-group\": \"../../checkbox-group/checkbox-group\",\n  "
  },
  {
    "path": "src/components/checkbox/__test__/index.wxml",
    "chars": 669,
    "preview": "<mp-cells title=\"单选列表项\">\n    <mp-checkbox-group class=\"radio-group\" prop=\"radio\" multi=\"{{false}}\" bindchange=\"radioChan"
  },
  {
    "path": "src/components/checkbox/checkbox.json",
    "chars": 195,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {\n        \"mp-cell\": \"../cell/cell"
  },
  {
    "path": "src/components/checkbox/checkbox.less",
    "chars": 848,
    "preview": "@import (reference) '~weui-wxss/src/style/widget/weui-cell/weui-check/weui-radio.less';\n@import (reference) '~weui-wxss/"
  },
  {
    "path": "src/components/checkbox/checkbox.ts",
    "chars": 1892,
    "preview": "Component({\n    options: {\n        multipleSlots: true\n    },\n    properties: {\n        multi: {\n            type: Boole"
  },
  {
    "path": "src/components/checkbox/checkbox.wxml",
    "chars": 898,
    "preview": "<label bindtap=\"checkedChange\">\n  <mp-cell\n    has-footer=\"{{!multi}}\"\n    has-header=\"{{multi}}\"\n    ext-class=\"weui-ch"
  },
  {
    "path": "src/components/checkbox-group/checkbox-group.json",
    "chars": 132,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {\n        \"mp-cells\": \"../cells/ce"
  },
  {
    "path": "src/components/checkbox-group/checkbox-group.less",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/components/checkbox-group/checkbox-group.ts",
    "chars": 3318,
    "preview": "// import cells from \"../cells/cells\"\n\nComponent({\n    options: {\n        multipleSlots: true\n    },\n    properties: {\n "
  },
  {
    "path": "src/components/checkbox-group/checkbox-group.wxml",
    "chars": 221,
    "preview": "<checkbox-group wx:if=\"{{multi}}\" class=\"{{extClass}}\" bindchange=\"checkedChange\">\n    <slot></slot>\n</checkbox-group>\n<"
  },
  {
    "path": "src/components/dialog/__test__/__snapshots__/dialog.test.js.snap",
    "chars": 2849,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`dialog basic dialog 1`] = `\n<main>\n  <wx-root-portal\n    enable=\"{{"
  },
  {
    "path": "src/components/dialog/__test__/dialog.test.js",
    "chars": 1311,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('dialog', () => {\n    let id\n\n    beforeAl"
  },
  {
    "path": "src/components/dialog/__test__/index.js",
    "chars": 377,
    "preview": "Component({\n    data: {\n        buttonValue: '',\n        closed: false,\n        buttons: [{ text: '取消' }, { text: '确定' }"
  },
  {
    "path": "src/components/dialog/__test__/index.json",
    "chars": 91,
    "preview": "{\n    \"component\": true,\n    \"usingComponents\": {\n        \"mp-dialog\": \"..//dialog\"\n    }\n}"
  },
  {
    "path": "src/components/dialog/__test__/index.wxml",
    "chars": 170,
    "preview": "<mp-dialog\n    class=\"dialog\"\n    title=\"dialog title\"\n    show=\"{{!closed}}\"\n    buttons=\"{{buttons}}\"\n    bindbuttonta"
  },
  {
    "path": "src/components/dialog/dialog.json",
    "chars": 84,
    "preview": "{\n  \"component\": true,\n  \"styleIsolation\": \"apply-shared\",\n  \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/dialog/dialog.less",
    "chars": 465,
    "preview": ".weui-dialog__root {\n  position: relative;\n  z-index: 1000;\n}\n\n.weui-dialog__title:focus {\n  outline: none;\n}\n\n\n\n// FIXM"
  },
  {
    "path": "src/components/dialog/dialog.ts",
    "chars": 2331,
    "preview": "Component({\n    options: {\n        virtualHost: true,\n        multipleSlots: true // 在组件定义时的选项中启用多slot支持\n    },\n    prop"
  },
  {
    "path": "src/components/dialog/dialog.wxml",
    "chars": 1565,
    "preview": "<template name=\"body\">\n  <view class=\"weui-dialog {{extClass}} {{innerShow ? 'weui-animate-fade-in' : 'weui-animate-fade"
  },
  {
    "path": "src/components/form/__test__/__snapshots__/form.test.ts.snap",
    "chars": 23246,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`form basic 1`] = `\n<main>\n  <mp-form\n    id=\"form\"\n  >\n    <wx-view"
  },
  {
    "path": "src/components/form/__test__/comp/index.json",
    "chars": 307,
    "preview": "{\n    \"component\": true,\n    \"usingComponents\": {\n        \"mp-cells\": \"../../../cells/cells\",\n        \"mp-cell\": \"../../"
  },
  {
    "path": "src/components/form/__test__/comp/index.ts",
    "chars": 5454,
    "preview": "Component({\n    data: {\n        radioItems: [\n            { name: 'cell standard', value: '0', checked: true },\n        "
  },
  {
    "path": "src/components/form/__test__/comp/index.wxml",
    "chars": 4090,
    "preview": "<mp-form id=\"form\" rules=\"{{rules}}\" models=\"{{formData}}\">\n    <mp-cells title=\"单选列表项\">\n        <mp-checkbox-group prop"
  },
  {
    "path": "src/components/form/__test__/form-validator.test.ts",
    "chars": 1469,
    "preview": "import FormValidator from '../form-validator'\n\ndescribe('form-validator', () => {\n    test('basic usage', async () => {\n"
  },
  {
    "path": "src/components/form/__test__/form.test.ts",
    "chars": 677,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('form', () => {\n    let id: string\n\n    be"
  },
  {
    "path": "src/components/form/__test__/validator.test.ts",
    "chars": 4133,
    "preview": "import Validators from '../validator'\n\ndescribe('required', () => {\n    test('success', () => {\n        expect(Validator"
  },
  {
    "path": "src/components/form/form-validator.ts",
    "chars": 4716,
    "preview": "import Validator from './validator'\nimport { diff } from '../utils/object'\n\nconst toString = Object.prototype.toString\nc"
  },
  {
    "path": "src/components/form/form.json",
    "chars": 90,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/form/form.ts",
    "chars": 5214,
    "preview": "import FormValidator from './form-validator'\nimport { diffObject } from '../utils/object'\n\nfunction linked(target) {\n   "
  },
  {
    "path": "src/components/form/form.wxml",
    "chars": 53,
    "preview": "<view class=\"{{extClass}}\">\n    <slot></slot>\n</view>"
  },
  {
    "path": "src/components/form/validator.ts",
    "chars": 5821,
    "preview": "import { sprintf } from '../utils/string'\n\nconst defaultMessage = {\n    required: '%s必填',\n    minlength: '长度最少为%s',\n    "
  },
  {
    "path": "src/components/form-page/__test__/__snapshots__/form-page.test.ts.snap",
    "chars": 1634,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`form-page basic 1`] = `\n<main>\n  <mp-form-page>\n    <wx-view\n      "
  },
  {
    "path": "src/components/form-page/__test__/form-page.test.ts",
    "chars": 390,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('form-page', () => {\n    const id = simula"
  },
  {
    "path": "src/components/form-page/__test__/index.js",
    "chars": 14,
    "preview": "Component({})\n"
  },
  {
    "path": "src/components/form-page/__test__/index.json",
    "chars": 114,
    "preview": "{\n    \"usingComponents\": {\n        \"mp-form-page\": \"../form-page\",\n        \"mp-cells\": \"../../cells/cells\"\n    }\n}"
  },
  {
    "path": "src/components/form-page/__test__/index.wxml",
    "chars": 287,
    "preview": "<mp-form-page title=\"表单结构\" subtitle=\"展示表单页面的信息结构样式, 分别由头部区域/控件区域/提示区域/操作区域和底部信息区域组成。\">\n    <view>\n        <mp-cells titl"
  },
  {
    "path": "src/components/form-page/form-page.json",
    "chars": 84,
    "preview": "{\n  \"component\": true,\n  \"styleIsolation\": \"apply-shared\",\n  \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/form-page/form-page.less",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/components/form-page/form-page.ts",
    "chars": 922,
    "preview": "Component({\n    options: {\n        multipleSlots: true\n    },\n    properties: {\n        title: {\n            // Msg 标题\n "
  },
  {
    "path": "src/components/form-page/form-page.wxml",
    "chars": 823,
    "preview": "<view class=\"weui-form\">\n    <block wx:if=\"{{title || subtitle}}\">\n      <view class=\"weui-form__text-area\">\n          <"
  },
  {
    "path": "src/components/gallery/__test__/__snapshots__/gallery.test.ts.snap",
    "chars": 6018,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`gallery basic 1`] = `\n<mp-gallery\n  delete=\"{{true}}\"\n  id=\"gallery"
  },
  {
    "path": "src/components/gallery/__test__/gallery.test.ts",
    "chars": 2299,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('gallery', () => {\n    const gallery = sim"
  },
  {
    "path": "src/components/gallery/gallery.json",
    "chars": 90,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/gallery/gallery.less",
    "chars": 460,
    "preview": ".weui-gallery__opr {\n  padding-bottom: 0;\n}\n\n.weui-gallery__del {\n  padding-bottom: calc(16px + env(safe-area-inset-bott"
  },
  {
    "path": "src/components/gallery/gallery.ts",
    "chars": 2398,
    "preview": "Component({\n    properties: {\n        imgUrls: {\n            type: Array,\n            value: [],\n            observer(ne"
  },
  {
    "path": "src/components/gallery/gallery.wxml",
    "chars": 1408,
    "preview": "<template name=\"body\">\n  <view\n    class=\"weui-gallery {{innerShow ? 'weui-animate-fade-in' : 'weui-animate-fade-out'}} "
  },
  {
    "path": "src/components/grids/__test__/__snapshots__/grid.test.ts.snap",
    "chars": 7207,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`grids basic 1`] = `\n<main>\n  <mp-grids>\n    <wx-view\n      class=\"w"
  },
  {
    "path": "src/components/grids/__test__/grid.test.ts",
    "chars": 844,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('grids', () => {\n    const grids = simulat"
  },
  {
    "path": "src/components/grids/grids.json",
    "chars": 84,
    "preview": "{\n  \"component\": true,\n  \"styleIsolation\": \"apply-shared\",\n  \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/grids/grids.less",
    "chars": 244,
    "preview": ".weui-grid .weui-grid__icon_img {\n  width: 100%;\n  height: 100%;\n}\n\n/** skyline 不支持 float: left */\n.weui-grids {\n    dis"
  },
  {
    "path": "src/components/grids/grids.ts",
    "chars": 1222,
    "preview": "const defaultGridProps = {\n    target: 'self',\n    url: '',\n    openType: 'navigate',\n    delta: 1,\n    appId: '',\n    p"
  },
  {
    "path": "src/components/grids/grids.wxml",
    "chars": 1015,
    "preview": "<view class=\"weui-grids {{extClass}}\">\n  <block wx:for=\"{{innerGrids}}\" wx:key=\"index\">\n    <!-- 把 navigator 改成 view,因为这"
  },
  {
    "path": "src/components/half-screen-dialog/__test__/__snapshots__/half-screen-dialog.test.ts.snap",
    "chars": 2809,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`grids basic 1`] = `\n<mp-half-screen-dialog\n  id=\"dialog\"\n  bind:but"
  },
  {
    "path": "src/components/half-screen-dialog/__test__/half-screen-dialog.test.ts",
    "chars": 2275,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('grids', () => {\n    const halfScreenDialo"
  },
  {
    "path": "src/components/half-screen-dialog/half-screen-dialog.json",
    "chars": 84,
    "preview": "{\n  \"component\": true,\n  \"styleIsolation\": \"apply-shared\",\n  \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/half-screen-dialog/half-screen-dialog.less",
    "chars": 1051,
    "preview": ".weui-half-screen-dialog__hd__main:focus {\n    outline: none;\n}\n\n.weui-dialog__root {\n    position: absolute;\n    z-inde"
  },
  {
    "path": "src/components/half-screen-dialog/half-screen-dialog.ts",
    "chars": 2302,
    "preview": "Component({\n    options: {\n        multipleSlots: true // 在组件定义时的选项中启用多slot支持\n    },\n    properties: {\n        closabled"
  },
  {
    "path": "src/components/half-screen-dialog/half-screen-dialog.wxml",
    "chars": 2836,
    "preview": "<template name=\"body\">\n  <wxs module=\"wxs\">\n    function onMaskTouchmove () {\n      return false\n    }\n    module.export"
  },
  {
    "path": "src/components/icon/__test__/__snapshots__/icon.test.ts.snap",
    "chars": 15159,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`grids basic 1`] = `\n<main>\n  <wx-view\n    class=\" weui-icon\"\n    st"
  },
  {
    "path": "src/components/icon/__test__/icon.test.ts",
    "chars": 803,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('grids', () => {\n    const icon = simulate"
  },
  {
    "path": "src/components/icon/base64.ts",
    "chars": 2161,
    "preview": "/* eslint-disable */\nvar b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\nvar cb_encode = "
  },
  {
    "path": "src/components/icon/icon.json",
    "chars": 84,
    "preview": "{\n  \"component\": true,\n  \"styleIsolation\": \"apply-shared\",\n  \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/icon/icon.less",
    "chars": 299,
    "preview": ".weui-icon {\n  vertical-align: middle;\n  display: inline-block;\n  background: black;\n  mask-repeat: no-repeat;\n  -webkit"
  },
  {
    "path": "src/components/icon/icon.ts",
    "chars": 1314,
    "preview": "import Base64 from './base64'\nimport iconData from './icondata'\n\nconst getFixedIconType = function (type: string): strin"
  },
  {
    "path": "src/components/icon/icon.wxml",
    "chars": 503,
    "preview": "<wxs module=\"utils\">\n    var double = function(a) {\n        return 2*a\n    };\n    var ifSpecialIcon = function(v) {\n    "
  },
  {
    "path": "src/components/icon/icondata.ts",
    "chars": 207261,
    "preview": "/* eslint-disable */\nexport default {\n    \"add-friends\": { outline: `<?xml version=\"1.0\" encoding=\"UTF-8\"?><svg width=\"2"
  },
  {
    "path": "src/components/index.json",
    "chars": 833,
    "preview": "{\n  \"usingComponents\": {\n    \"actionsheet\": \"./actionsheet/actionsheet\",\n    \"form-page\": \"./form-page/form-page\",\n    \""
  },
  {
    "path": "src/components/index.less",
    "chars": 73,
    "preview": "@import (css) '~weui-wxss/dist/style/weui.wxss';\n@import './patch.less';\n"
  },
  {
    "path": "src/components/index.ts",
    "chars": 81,
    "preview": "// import FormValidator from './form/form-validator'\n\nexport // FormValidator\n{}\n"
  },
  {
    "path": "src/components/index.wxml",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/components/loading/__test__/__snapshots__/loading.test.ts.snap",
    "chars": 437,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`loading basic 1`] = `\n<main>\n  <wx-view\n    class=\"wx_loading_view "
  },
  {
    "path": "src/components/loading/__test__/loading.test.ts",
    "chars": 849,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('loading', () => {\n    const loading = sim"
  },
  {
    "path": "src/components/loading/loading.json",
    "chars": 90,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/loading/loading.less",
    "chars": 375,
    "preview": ".wx_loading_view{\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  overflow: hidden;\n\n  &__hide {\n  "
  },
  {
    "path": "src/components/loading/loading.ts",
    "chars": 627,
    "preview": "Component({\n    properties: {\n        extClass: {\n            type: String,\n            value: ''\n        },\n        sho"
  },
  {
    "path": "src/components/loading/loading.wxml",
    "chars": 598,
    "preview": "<view class=\"wx_loading_view {{animated ? 'wx_loading_view__animated' : ''}} {{!show ? 'wx_loading_view__hide' : ''}} {{"
  },
  {
    "path": "src/components/msg/__test__/__snapshots__/msg.test.ts.snap",
    "chars": 2338,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`msg basic 1`] = `\n<main>\n  <mp-msg>\n    <wx-view\n      class=\"weui-"
  },
  {
    "path": "src/components/msg/__test__/msg.test.ts",
    "chars": 1585,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('msg', () => {\n    const msg = simulate.lo"
  },
  {
    "path": "src/components/msg/msg.json",
    "chars": 84,
    "preview": "{\n  \"component\": true,\n  \"styleIsolation\": \"apply-shared\",\n  \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/msg/msg.less",
    "chars": 60,
    "preview": ".weui-msg__icon-img {\n  width: 190rpx;\n  height: 190rpx;\n}\n\n"
  },
  {
    "path": "src/components/msg/msg.ts",
    "chars": 716,
    "preview": "Component({\n    options: {\n        multipleSlots: true\n    },\n    properties: {\n        title: {\n            // Msg 标题\n "
  },
  {
    "path": "src/components/msg/msg.wxml",
    "chars": 867,
    "preview": "<view class=\"weui-msg {{extClass}}\">\n  <view class=\"weui-msg__icon-area\">\n    <icon type=\"{{type}}\" size=\"{{size}}\" wx:i"
  },
  {
    "path": "src/components/navigation-bar/__test__/__snapshots__/navigation-bar.test.ts.snap",
    "chars": 1554,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`navigation-bar basic 1`] = `\n<main>\n  <mp-navigation-bar>\n    <wx-v"
  },
  {
    "path": "src/components/navigation-bar/__test__/navigation-bar.test.ts",
    "chars": 773,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('navigation-bar', () => {\n    const naviga"
  },
  {
    "path": "src/components/navigation-bar/navigation-bar.json",
    "chars": 84,
    "preview": "{\n  \"component\": true,\n  \"styleIsolation\": \"apply-shared\",\n  \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/navigation-bar/navigation-bar.less",
    "chars": 275,
    "preview": ".weui-navigation-bar__btn_goback_wrapper {\n  padding: 11px 18px 11px 16px;\n  margin: -11px -18px -11px -16px;\n}\n\n.weui-n"
  },
  {
    "path": "src/components/navigation-bar/navigation-bar.ts",
    "chars": 2297,
    "preview": "Component({\n    options: {\n        multipleSlots: true // 在组件定义时的选项中启用多slot支持\n    },\n    /**\n     * 组件的属性列表\n     */\n    "
  },
  {
    "path": "src/components/navigation-bar/navigation-bar.wxml",
    "chars": 2161,
    "preview": "<view class=\"weui-navigation-bar {{extClass}}\">\n  <view class=\"weui-navigation-bar__placeholder {{ios ? 'ios' : 'android"
  },
  {
    "path": "src/components/patch.less",
    "chars": 2980,
    "preview": "@import (reference) \"~weui-wxss/src/style/base/fn.less\";\n\n/* weui 用了标签写法,skyline 下部分声明不生效,所以重新声明一次 */\n/* root-portal-con"
  },
  {
    "path": "src/components/searchbar/__test__/__snapshots__/searchbar.test.ts.snap",
    "chars": 10574,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`searchbar basic 1`] = `\n<main>\n  <mp-searchbar\n    id=\"searchbar\"\n "
  },
  {
    "path": "src/components/searchbar/__test__/searchbar.test.ts",
    "chars": 2448,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('searchbar', () => {\n    const searchbar ="
  },
  {
    "path": "src/components/searchbar/searchbar.json",
    "chars": 167,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {\n        \"mp-cells\": \"../cells/ce"
  },
  {
    "path": "src/components/searchbar/searchbar.less",
    "chars": 590,
    "preview": ".weui-search-bar__label text {\n  display: inline-block;\n  font-size: 14px;\n  vertical-align: middle;\n}\n\n.weui-search-bar"
  },
  {
    "path": "src/components/searchbar/searchbar.ts",
    "chars": 3396,
    "preview": "Component({\n    properties: {\n        extClass: {\n            type: String,\n            value: ''\n        },\n        foc"
  },
  {
    "path": "src/components/searchbar/searchbar.wxml",
    "chars": 1978,
    "preview": "<view class=\"weui-search-bar {{searchState ? 'weui-search-bar_focusing' : ''}} {{extClass}}\">\n    <view\n        class=\"w"
  },
  {
    "path": "src/components/slideview/__test__/__snapshots__/slideview.test.ts.snap",
    "chars": 3914,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`slideview basic 1`] = `\n<main\n  bind:buttontap=\"slideButtonTap\"\n>\n "
  },
  {
    "path": "src/components/slideview/__test__/slideview.test.ts",
    "chars": 1446,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\n/**\n * TODO slideview 使用了 wxs 事件绑定,等待 j-component 支"
  },
  {
    "path": "src/components/slideview/slideview-skyline.json",
    "chars": 84,
    "preview": "{\n  \"component\": true,\n  \"styleIsolation\": \"apply-shared\",\n  \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/slideview/slideview-skyline.less",
    "chars": 293,
    "preview": ".weui-slideview__btn {\n    flex: 1;\n    min-width: 0;\n}\n.weui-slideview_icon .weui-slideview__btn {\n    flex: none;\n}\n.w"
  },
  {
    "path": "src/components/slideview/slideview-skyline.ts",
    "chars": 6778,
    "preview": "Component({\n    /**\n     * 组件的属性列表\n     */\n    options: {\n        multipleSlots: true,\n        virtualHost: true\n    },\n"
  },
  {
    "path": "src/components/slideview/slideview-skyline.wxml",
    "chars": 1019,
    "preview": "<!-- slide-view/slide-view.wxml -->\n<view class=\"weui-slideview weui-movable-view {{icon ? 'weui-slideview_icon' : ''}} "
  },
  {
    "path": "src/components/slideview/slideview.json",
    "chars": 134,
    "preview": "{\n  \"component\": true,\n  \"styleIsolation\": \"apply-shared\",\n  \"usingComponents\": {\n    \"slideview-skyline\": \"./slideview-"
  },
  {
    "path": "src/components/slideview/slideview.less",
    "chars": 177,
    "preview": ".weui-slideview__btn {\n    flex: 1;\n    min-width: 0;\n}\n.weui-slideview_icon .weui-slideview__btn {\n    flex: none;\n}\n.w"
  },
  {
    "path": "src/components/slideview/slideview.ts",
    "chars": 3084,
    "preview": "Component({\n    /**\n     * 组件的属性列表\n     */\n    options: {\n        multipleSlots: true\n    },\n    properties: {\n        e"
  },
  {
    "path": "src/components/slideview/slideview.wxml",
    "chars": 1920,
    "preview": "<!-- slide-view/slide-view.wxml -->\n<wxs module=\"handler\" src=\"./slideview.wxs\"></wxs>\n<view wx:if=\"{{renderer === 'webv"
  },
  {
    "path": "src/components/slideview/slideview.wxs",
    "chars": 8346,
    "preview": "/* eslint-disable */\nvar touchstart = function(event, ownerInstance) {\n    var ins = event.instance\n    var st = ins.get"
  },
  {
    "path": "src/components/tabbar/__test__/__snapshots__/tabbar.test.ts.snap",
    "chars": 3937,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`tabbar basic 1`] = `\n<main>\n  <mp-tabbar\n    id=\"tabbar\"\n    style="
  },
  {
    "path": "src/components/tabbar/__test__/tabbar.test.ts",
    "chars": 1988,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('tabbar', () => {\n    const tabbar = simul"
  },
  {
    "path": "src/components/tabbar/tabbar.json",
    "chars": 132,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {\n        \"mp-badge\": \"../badge/ba"
  },
  {
    "path": "src/components/tabbar/tabbar.less",
    "chars": 419,
    "preview": "@import \"~weui-wxss/src/style/base/fn.less\";\n\n// @media only screen and (min-width: 450px) {\n//   .weui-tabbar__reactive"
  },
  {
    "path": "src/components/tabbar/tabbar.ts",
    "chars": 696,
    "preview": "Component({\n    properties: {\n        extClass: {\n            type: String,\n            value: ''\n        },\n        lis"
  },
  {
    "path": "src/components/tabbar/tabbar.wxml",
    "chars": 1040,
    "preview": "<view class=\"weui-tabbar {{reactive ? 'weui-tabbar__reactive' : ''}} {{extClass}}\" aria-role=\"tablist\">\n  <!-- 选中的时候往 we"
  },
  {
    "path": "src/components/toptips/__test__/__snapshots__/toptips.test.ts.snap",
    "chars": 929,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`toptips auto hide 1`] = `\n<main>\n  <mp-toptips>\n    <wx-root-portal"
  },
  {
    "path": "src/components/toptips/__test__/toptips.test.ts",
    "chars": 1283,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n\ndescribe('toptips', () => {\n    const toptips = sim"
  },
  {
    "path": "src/components/toptips/toptips.json",
    "chars": 90,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {}\n}"
  },
  {
    "path": "src/components/toptips/toptips.less",
    "chars": 230,
    "preview": ".weui-toptips {\n    display: flex;\n}\n.weui-toptips_success {\n    background-color: var(--weui-BRAND);\n}\n.weui-toptips_er"
  },
  {
    "path": "src/components/toptips/toptips.ts",
    "chars": 2230,
    "preview": "const typeClassMap = {\n    warn: 'weui-toptips_warn',\n    info: 'weui-toptips_info',\n    success: 'weui-toptips_success'"
  },
  {
    "path": "src/components/toptips/toptips.wxml",
    "chars": 635,
    "preview": "<template name=\"body\">\n    <view\n        aria-role=\"alert\"\n        class=\"weui-toptips weui-toptips_warn {{className}} {"
  },
  {
    "path": "src/components/uploader/__test__/__snapshots__/uploader.test.ts.snap",
    "chars": 9976,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`uploader basic 1`] = `\n<main>\n  <mp-uploader\n    id=\"uploader\"\n  >\n"
  },
  {
    "path": "src/components/uploader/__test__/uploader.test.ts",
    "chars": 4456,
    "preview": "import path from 'path'\nimport simulate from 'miniprogram-simulate'\n;(globalThis as any).wx = (globalThis as any).wx || "
  },
  {
    "path": "src/components/uploader/uploader.json",
    "chars": 139,
    "preview": "{\n    \"component\": true,\n    \"styleIsolation\": \"apply-shared\",\n    \"usingComponents\": {\n        \"mp-gallery\": \"../galler"
  },
  {
    "path": "src/components/uploader/uploader.less",
    "chars": 201,
    "preview": ".weui-uploader__bd,\n.weui-uploader__files {\n  display: flex; /* skyline 不支持 float */\n  flex-direction: row;\n}\n\n.weui-upl"
  },
  {
    "path": "src/components/uploader/uploader.ts",
    "chars": 8156,
    "preview": "Component({\n    options: {\n        \n    },\n    properties: {\n        title: {\n            type: String,\n            valu"
  }
]

// ... and 154 more files (download for full content)

About this extraction

This page contains the full source code of the wechat-miniprogram/weui-miniprogram GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 354 files (640.9 KB), approximately 231.0k tokens, and a symbol index with 217 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!