├── .babelrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .prettierrc.js ├── LICENSE.md ├── README.md ├── babel.config.js ├── docs ├── .nojekyll ├── README.md ├── SUMMARY.md ├── advanced │ ├── dot.md │ ├── layout.md │ └── query.md ├── api │ ├── component.md │ ├── index.md │ └── next.md ├── bugfix │ └── performance.md ├── demo │ ├── consumer.md │ ├── consumerExtra.md │ ├── customField.md │ ├── dynamicColumns.md │ ├── expand.md │ ├── expandTable.md │ ├── fields.md │ ├── formily.md │ ├── hooks.md │ ├── layout.md │ ├── mode.md │ ├── multiFilter.md │ ├── practise.md │ ├── selection.md │ ├── sorterTable.md │ └── syncParams.md ├── formily │ ├── basic.md │ ├── crud.md │ ├── ds.md │ ├── dynamicColumns.md │ ├── empty.md │ ├── expand.md │ ├── listConsumer.md │ ├── nested.md │ ├── params.md │ ├── tableSelection.md │ └── tableSort.md ├── home.less ├── home.tsx ├── iframe.html ├── index.html ├── interface │ ├── actions.md │ ├── communicate.md │ ├── fields.md │ ├── layout.md │ ├── lifecycle.md │ ├── multiple.md │ └── query.md ├── quickStart.md ├── schema │ ├── index.md │ └── practise.md └── test.md ├── jest.config.js ├── lerna.json ├── package.json ├── packages ├── .eslintrc ├── antd-components │ ├── .npmignore │ ├── LICENSE.md │ ├── README.md │ ├── README.zh-cn.md │ ├── build.ts │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── index.spec.tsx │ │ ├── components │ │ │ ├── Button.tsx │ │ │ ├── Clear.tsx │ │ │ ├── Consumer.tsx │ │ │ ├── ExpandContainer.tsx │ │ │ ├── ExpandTrigger.tsx │ │ │ ├── List.tsx │ │ │ ├── MultipleProvider.tsx │ │ │ ├── Pagination.tsx │ │ │ ├── Reset.tsx │ │ │ ├── Search.tsx │ │ │ ├── Selection.tsx │ │ │ ├── Table.tsx │ │ │ └── ToggleTrigger.tsx │ │ ├── hooks │ │ │ ├── index.ts │ │ │ └── useAntdList.ts │ │ ├── index.ts │ │ ├── shared.ts │ │ ├── style.ts │ │ └── types.ts │ └── tsconfig.json ├── antd │ ├── .npmignore │ ├── LICENSE.md │ ├── README.md │ ├── README.zh-cn.md │ ├── build.ts │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── index.spec.tsx │ │ ├── components │ │ │ ├── Filter.tsx │ │ │ ├── Layout.tsx │ │ │ └── SchemaList.tsx │ │ ├── fields │ │ │ └── index.tsx │ │ ├── hooks │ │ │ └── useAntdList.ts │ │ ├── index.ts │ │ ├── style.ts │ │ └── types.ts │ └── tsconfig.json ├── core │ ├── .npmignore │ ├── LICENSE.md │ ├── README.md │ ├── README.zh-cn.md │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ ├── core.spec.ts │ │ │ └── index.spec.ts │ │ ├── defaultQuery.ts │ │ ├── fetch.ts │ │ ├── index.ts │ │ ├── model │ │ │ ├── core.ts │ │ │ ├── lazyAction.ts │ │ │ └── lifeCycles.ts │ │ ├── types.ts │ │ └── util │ │ │ └── index.ts │ └── tsconfig.json ├── next-components │ ├── .npmignore │ ├── LICENSE.md │ ├── README.md │ ├── README.zh-cn.md │ ├── build.ts │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── index.spec.tsx │ │ ├── components │ │ │ ├── Button.tsx │ │ │ ├── Clear.tsx │ │ │ ├── Consumer.tsx │ │ │ ├── ExpandContainer.tsx │ │ │ ├── ExpandTrigger.tsx │ │ │ ├── List.tsx │ │ │ ├── MultipleProvider.tsx │ │ │ ├── Pagination.tsx │ │ │ ├── Reset.tsx │ │ │ ├── Search.tsx │ │ │ ├── Selection.tsx │ │ │ ├── Sorter.tsx │ │ │ ├── Table.tsx │ │ │ └── ToggleTrigger.tsx │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useAList.ts │ │ │ └── useNextList.ts │ │ ├── index.ts │ │ ├── shared.ts │ │ ├── style.ts │ │ └── types.ts │ └── tsconfig.json ├── next │ ├── .npmignore │ ├── LICENSE.md │ ├── README.md │ ├── README.zh-cn.md │ ├── build.ts │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── index.spec.tsx │ │ ├── components │ │ │ ├── Filter.tsx │ │ │ ├── Layout.tsx │ │ │ └── SchemaList.tsx │ │ ├── fields │ │ │ └── index.tsx │ │ ├── index.ts │ │ ├── style.ts │ │ └── types.ts │ └── tsconfig.json ├── react-schema-renderer │ ├── .npmignore │ ├── LICENSE.md │ ├── README.md │ ├── README.zh-cn.md │ ├── mock.json │ ├── package.json │ ├── src │ │ ├── __tests__ │ │ │ └── index.spec.tsx │ │ ├── index.tsx │ │ ├── shared.tsx │ │ ├── types.ts │ │ └── util │ │ │ └── index.tsx │ └── tsconfig.json └── react │ ├── .npmignore │ ├── LICENSE.md │ ├── README.md │ ├── README.zh-cn.md │ ├── mock.json │ ├── package.json │ ├── src │ ├── __tests__ │ │ └── index.spec.tsx │ ├── components │ │ ├── ConnectProvider.tsx │ │ ├── Consumer.tsx │ │ ├── ExpandContainer.tsx │ │ ├── ExpandTrigger.tsx │ │ ├── FieldProvider.tsx │ │ ├── FilterProvider.tsx │ │ ├── Layout.tsx │ │ ├── ListProvider.tsx │ │ ├── LoadingProvider.tsx │ │ ├── MultipleProvider.tsx │ │ ├── PaginationProvider.tsx │ │ ├── Selection.tsx │ │ ├── SorterProvider.tsx │ │ ├── TableProvider.tsx │ │ ├── Toggle.tsx │ │ └── ToggleProvider.tsx │ ├── context │ │ ├── filterMode.ts │ │ ├── index.ts │ │ ├── listDomain.ts │ │ ├── listProps.ts │ │ ├── multiple.ts │ │ ├── table.ts │ │ └── toggle.ts │ ├── hooks │ │ ├── index.ts │ │ ├── useConsumer.ts │ │ ├── useExpand.ts │ │ ├── useExpandContainer.ts │ │ ├── useFilter.ts │ │ ├── useFilterItem.ts │ │ ├── useForceUpdate.ts │ │ ├── useList.ts │ │ ├── useListDomain.ts │ │ ├── useLoading.ts │ │ ├── useMutlipleProvider.ts │ │ ├── usePagination.ts │ │ ├── useTable.ts │ │ └── useToggle.ts │ ├── index.ts │ ├── shared.ts │ ├── types.ts │ └── utils │ │ └── index.ts │ └── tsconfig.json ├── scripts ├── build.ts ├── doc-renderer.js ├── docs.js ├── global.js ├── jest.base.js └── validate-commit-msg.js ├── tsconfig.build.json ├── tsconfig.jest.json └── tsconfig.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [["@babel/preset-env", { "loose": true }], "@babel/preset-react"], 3 | "plugins": [ 4 | "@babel/plugin-transform-runtime", 5 | "@babel/plugin-proposal-class-properties", 6 | "@babel/plugin-syntax-dynamic-import", 7 | ["@babel/plugin-proposal-decorators", { "legacy": true }], 8 | "babel-plugin-dynamic-import-node" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [*.gradle] 14 | indent_size = 4 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | dist 4 | build 5 | coverage 6 | expected 7 | website 8 | gh-pages 9 | weex 10 | build.ts 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | }, 5 | "globals": { 6 | "sleep": true, 7 | "prettyFormat": true 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 10, 11 | "sourceType": "module", 12 | "ecmaFeatures": { 13 | "jsx": true 14 | } 15 | }, 16 | } -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: MainWorkflow 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - uses: actions/setup-node@v1 11 | with: 12 | node-version: "12.x" 13 | - name: Build Blog 14 | run: | 15 | npm install 16 | npm run bootstrap:ci 17 | npm run build:docs 18 | - name: setup aliyun oss 19 | uses: manyuanrong/setup-ossutil@master 20 | with: 21 | endpoint: "oss-cn-beijing.aliyuncs.com" 22 | access-key-id: ${{ secrets.OSS_KEY_ID }} 23 | access-key-secret: ${{ secrets.OSS_KEY_SECRET }} 24 | - name: Deply To OSS 25 | run: ossutil cp docs-build oss://alist-wiki/ -rf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | .DS_Store 4 | npm-debug.log 5 | lerna-debug.log 6 | npm-debug.log* 7 | package-lock.json 8 | lib/ 9 | esm/ 10 | temp_esm/ 11 | dist/ 12 | build/ 13 | coverage/ 14 | node_modules/ 15 | examples/test 16 | .idea/ 17 | .vscode/ 18 | TODO.md 19 | tsconfig.tsbuildinfo 20 | package/ 21 | package.zip -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: false, 3 | tabWidth: 2, 4 | singleQuote: true, 5 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present, Alibaba Group Holding Limited. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AList 2 | 3 | ## 备注 4 | 5 | 原有体系下的 NoForm 将不再更新,目前版本仍可继续运行并持续维护。表单场景,推荐使用 [Formily](https://github.com/alibaba/formily) 6 | 7 | 8 | ## 背景 9 | 10 | 列表是中后台的入口场景,需求的量级与表单相同,但是对于效率和性能的体验则没有那么高,相对来说可定制性和拓展性也相对比较规范。 11 | 在 Fusion/Ant-Design 的 Table 已经成为业界事实标准的情况下,且 [Formily](https://github.com/alibaba/formily) 表单方案作为搜索区域的不二之选时,通过对这些方案的整合,可以快速实现标准化的列表场景。 12 | 13 | 同时AList支持 `JSON Schema` 协议渲染,可通过数据驱动快速开发。 14 | 15 | ## 特性 16 | 17 | 🚀 内置Formily作为搜索区域方案,性能及功能强大 18 | 19 | 💡 支持 Ant Design/Fusion Next 组件体系 20 | 21 | 🎨 支持JSON Schema 数据驱动方案 22 | 23 | 🏅 副作用逻辑独立管理,涵盖各种复杂联动校验逻辑 24 | 25 | 🌯 支持各种复杂布局方案 26 | 27 | ## 安装 28 | 29 | 使用 Ant Design: 30 | 31 | ```shell 32 | npm install --save antd @alist/antd 33 | ``` 34 | 35 | 使用 Fusion Design: 36 | 37 | ```shell 38 | npm install --save @alifd/next @alist/next 39 | ``` 40 | 41 | ## 文档 42 | 43 | https://alist.wiki/ 44 | 45 | 46 | ## LICENSE 47 | AList is open source software licensed as MIT. 48 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const json5 = require('json5') 4 | const cwd = process.cwd() 5 | 6 | module.exports = api => { 7 | if (api) api.cache(true) 8 | return json5.parse(fs.readFileSync(path.resolve(cwd, '.babelrc'), 'utf8')) 9 | } 10 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/alist/d03650f3a7cdfb4c4ad71142d10d79d8ddfd3bf4/docs/.nojekyll -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## 背景 2 | 3 | 列表可以说是中后台的入口场景,需求的量级与表单相同,但是对于效率和性能的体验则没有那么高,可定制性和拓展性也相对比较规范。同时 `Fusion/Ant-Design` 的 `Table` 已经成为事实标准,而搜索区域也有 [Formily](https://formilyjs.org/) 作为表单领域的佼佼者,这也使得列表框架是集大成者。它的主要作用是整合这些标准,并且使得编写列表更加快速和标准。 4 | 5 | ## 特点 6 | 7 | ### 1. 支持定制请求规范 8 | 9 | `AList` 本身指定请求前后的规范,并且通过开放这个过程的各项钩子满足业务定制,查看 [列表请求规范](#) 和 [定制请求](#) 了解更多。 10 | 11 | ### 2. 支持定制样式规范 12 | 13 | `AList` 提供众多的布局方案,满足大部分场景,对于个性化方案也提供了定制的能力,查看 [定制布局](#) 了解更多。 14 | 15 | ### 3. 支持Schema数据驱动 16 | 17 | 开发者可以通过 `JSON-Schema` 进行开发,效果与 `JSX` 开发完全一致。查看 [列表Schema规范](#) 及 [列表Schema实战](#) 了解更多。 18 | 19 | ### 4. 支持hooks开发 20 | 21 | 开发者可以选择配合的组件开发,无须强制使用 `AList` 的组件也可以得到标准化的列表能力。 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | - [首页](./home.tsx) 2 | - 开发指南 3 | - [介绍](./README.md) 4 | - [快速开始](./quickStart.md) 5 | - 概念/术语理解 6 | - [理解请求规范](./interface/query.md) 7 | - [理解列表生命周期](./interface/lifecycle.md) 8 | - [理解 actions/effects](./interface/actions.md) 9 | - [搜索区域组件](./interface/fields.md) 10 | - [搜索区域布局](./interface/layout.md) 11 | - [理解多实例列表](./interface/multiple.md) 12 | - [理解组件间通信机制](./interface/communicate.md) 13 | - [理解 Schema](./schema/index.md) 14 | - 实践教程 15 | - [三种数据模式](./demo/mode.md) 16 | - [自定义搜索条件](./demo/customField.md) 17 | - [定制搜索条件及镜像搜索区域](./demo/multiFilter.md) 18 | - [搜索布局](./demo/layout.md) 19 | - [搜索区域展开收起](./demo/expand.md) 20 | - [Table 区域展开收起](./demo/expandTable.md) 21 | - [Table 排序](./demo/sorterTable.md) 22 | - [设置及消费筛选状态](./demo/selection.md) 23 | - [同步 URL 参数](./demo/syncParams.md) 24 | - [自定义消费组件](./demo/consumer.md) 25 | - [消费额外接口数据](./demo/consumerExtra.md) 26 | - [通过 Schema 渲染](./schema/practise.md) 27 | - [设置动态列](./demo/dynamicColumns.md) 28 | - 进阶教程 29 | - [实现自定义请求组件](./advanced/query.md) 30 | - [实现自定义静默埋点](./advanced/dot.md) 31 | - 结合 Formily 32 | - [快速开始](./formily/basic.md) 33 | - [搜索及 Table 区域展开收起](./formily/expand.md) 34 | - [手动控制数据源](./formily/ds.md) 35 | - [多实例嵌套展开收起](./formily/nested.md) 36 | - [Table 排序](./formily/tableSort.md) 37 | - [Table 筛选及消费](./formily/tableSelection.md) 38 | - [空状态](./formily/empty.md) 39 | - [自定义消费组件](./formily/listConsumer.md) 40 | - [设置动态列](./formily/dynamicColumns.md) 41 | - [同步 URL 参数](./formily/params.md) 42 | - API 手册 43 | - [API 列表](./api/index.md) 44 | - [组件列表](./api/component.md) 45 | - [GITHUB](https://github.com/alibaba/AList) 46 | -------------------------------------------------------------------------------- /docs/advanced/dot.md: -------------------------------------------------------------------------------- 1 | # 实现自定义静默埋点 2 | 3 | ## 覆盖式 4 | 5 | 1. 封装符合业务团队埋点规范的的能力。 6 | 7 | ```tsx 8 | import * as AList from '@alist/antd' 9 | 10 | const noop = () => {} 11 | const BizList = (props) => { 12 | const { effects = noop } = props 13 | return { 16 | // 请求前埋点 17 | $(ListLifeCycleTypes.ON_LIST_BEFORE_QUERY).subscribe(() => { 18 | 19 | }); 20 | 21 | // 请求后埋点,需要关心是否为空,和是否请求发生异常 22 | $(ListLifeCycleTypes.ON_LIST_AFTER_QUERY).subscribe(payload => { 23 | const { empty, hasError, error } = payload; 24 | 25 | }); 26 | 27 | // 请求前校验埋点 28 | $(ListLifeCycleTypes.ON_LIST_VALIDATE_END).subscribe(payload => { 29 | const { success, errors } = payload; 30 | }); 31 | 32 | // 点击清空埋点 33 | $(ListLifeCycleTypes.ON_LIST_CLEAR).subscribe(() => { 34 | 35 | }); 36 | 37 | // 点击重置埋点 38 | $(ListLifeCycleTypes.ON_LIST_RESET).subscribe(() => { 39 | 40 | }); 41 | 42 | effects($, actions); 43 | }} 44 | /> 45 | } 46 | 47 | export { 48 | ...AList, 49 | List: BizList 50 | } 51 | ``` 52 | 53 | 2. 由于 `BizList` 已经包含埋点功能,其他开发者无须单独埋点,直接使用即可 54 | 55 | ```tsx 56 | import { List } from 'biz-list' 57 | 58 | const App = () => { 59 | return 60 | } 61 | 62 | ``` -------------------------------------------------------------------------------- /docs/advanced/layout.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/alist/d03650f3a7cdfb4c4ad71142d10d79d8ddfd3bf4/docs/advanced/layout.md -------------------------------------------------------------------------------- /docs/advanced/query.md: -------------------------------------------------------------------------------- 1 | # 实现自定义请求组件 2 | 3 | 由于 `AList` 有自己的请求规范,有可能和开发者业务团队的标准有差异,此时需要包装有业务含义的请求组件。 4 | 5 | ## 覆盖式 6 | 7 | 1. 封装符合业务团队接口规范的 `BizList` 8 | 9 | ```tsx 10 | import * as AList from '@alist/antd' 11 | 12 | const bizFormatBefore = () => {} 13 | const bizFormatAfter = () => {} 14 | 15 | const BizList = (props) => { 16 | const { formatBefore, formatAfter } = props 17 | return 22 | } 23 | 24 | export { 25 | ...AList, 26 | List: BizList 27 | } 28 | ``` 29 | 30 | 2. 由于 `BizList` 已经是规范化处理完成,其他开发者只需要传入`url` 即可以 31 | 32 | ```tsx 33 | import { List } from 'biz-list' 34 | 35 | const App = () => { 36 | return 37 | } 38 | 39 | ``` -------------------------------------------------------------------------------- /docs/api/next.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/alist/d03650f3a7cdfb4c4ad71142d10d79d8ddfd3bf4/docs/api/next.md -------------------------------------------------------------------------------- /docs/demo/consumer.md: -------------------------------------------------------------------------------- 1 | # Consumer 2 | 3 | `` 为自定义消费组件,能够动态获取当前列表实例(ListActions),并使用API来渲染消费列表数据。 4 | 5 | > 默认 `selector` 为 *,即所有的变更都会引起重绘,可以选择指定的生命周期控制渲染次数。 6 | 7 | ```jsx 8 | import { createListActions, Consumer, List, Table, Pagination, Filter, Layout, Search, Clear } from '@alist/antd' 9 | import'antd/dist/antd.css' 10 | 11 | const actions = createListActions() 12 | 13 | const App = props => { 14 | const { children, ...others } = props 15 | const url = 'https://alist-wiki.oss-cn-beijing.aliyuncs.com/data.json' 16 | 17 | return ( 18 | 19 | 20 | {list => { 21 | const { currentPage, pageSize } = list.getPageData() 22 | const filterData = list.getFilterData() 23 | const paginationedData = list.getPaginationDataSource() 24 | const originData = list.getDataSource() 25 | return ( 26 |
27 | dataSource: {paginationedData.length}
28 | age: {filterData.age}
29 | username: {filterData.username}
30 | currentPage:{currentPage}
31 | pageSize:{pageSize}
32 |
33 | ) 34 | }} 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | Search 43 | Clear 44 | 45 | 46 | 47 | 48 | 49 |
50 |
51 | ) 52 | } 53 | 54 | ReactDOM.render(, document.getElementById('root')) 55 | ``` -------------------------------------------------------------------------------- /docs/demo/consumerExtra.md: -------------------------------------------------------------------------------- 1 | # Consumer 消费额外接口数据 2 | 3 | `` 为自定义消费组件,能够动态获取当前列表实例(ListActions),并使用 API 来渲染消费列表数据。 4 | 5 | > 默认 `selector` 为 \*,即所有的变更都会引起重绘,可以选择指定的生命周期控制渲染次数。 6 | 7 | ```jsx 8 | import { 9 | createListActions, 10 | Consumer, 11 | List, 12 | Table, 13 | Pagination, 14 | Filter, 15 | Layout, 16 | Search, 17 | Clear 18 | } from '@alist/antd' 19 | import 'antd/dist/antd.css' 20 | 21 | const actions = createListActions() 22 | window.ll = actions 23 | 24 | const App = props => { 25 | const { children, ...others } = props 26 | const url = 'https://alist-wiki.oss-cn-beijing.aliyuncs.com/extra.json' 27 | 28 | return ( 29 | 30 | 31 | {list => { 32 | const { extra } = list.getResponseData() 33 | return ( 34 |
35 | extraData: {JSON.stringify(extra)}
36 |
37 | ) 38 | }} 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | Search 47 | Clear 48 | 49 | 50 | 51 | 52 | 53 |
54 |
55 | ) 56 | } 57 | 58 | ReactDOM.render(, document.getElementById('root')) 59 | ``` 60 | -------------------------------------------------------------------------------- /docs/demo/expand.md: -------------------------------------------------------------------------------- 1 | # 展开收起 ExpandContainer/ExpandTrigger 2 | 3 | 通过 `` 可以控制被 `` 包裹的搜索组件。 4 | 5 | ```jsx 6 | import React from 'react' 7 | import { 8 | List, 9 | Table, 10 | Pagination, 11 | createListActions, 12 | Filter, 13 | Search, 14 | Clear, 15 | Layout, 16 | SchemaMarkupField, 17 | FormBlock, 18 | FormCard, 19 | SchemaForm, 20 | ExpandContainer, 21 | ExpandTrigger, 22 | } from '@alist/antd' 23 | import'antd/dist/antd.css' 24 | 25 | const actions = createListActions() 26 | const App = () => { 27 | const url = 'https://alist-wiki.oss-cn-beijing.aliyuncs.com/data.json' 28 | 29 | return ( 30 |
31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 搜索 45 | 重置 46 | 47 | 48 | 49 | 50 |
51 | ) 52 | } 53 | 54 | ReactDOM.render(, document.getElementById('root')) 55 | ``` -------------------------------------------------------------------------------- /docs/demo/expandTable.md: -------------------------------------------------------------------------------- 1 | ## Table区域展开收起 2 | 3 | ## Fusion-Next 4 | 5 | 通过 `ToggleTrigger` 可以快速实现 6 | 7 | ```jsx 8 | import React from 'react' 9 | import { 10 | List, Table, 11 | ToggleTrigger, 12 | } from '@alist/next' 13 | import '@alifd/next/dist/next.css' 14 | 15 | const App = () => { 16 | return
17 | 23 | (record.label + '-' + record.value)} 25 | hasExpandedRowCtrl={false} 26 | > 27 | 28 | 29 | { 30 | return 31 | }} /> 32 |
33 |
34 |
35 | } 36 | 37 | ReactDOM.render(, document.getElementById('root')) 38 | ``` 39 | 40 | ## Ant-Design 41 | 42 | 通过 `ToggleTrigger` 可以快速实现 43 | 44 | ```jsx 45 | import React from 'react' 46 | import { 47 | List, Table, Pagination, 48 | Filter, 49 | Layout, 50 | Search, 51 | Clear, 52 | createListActions, 53 | ListLifeCycleTypes, 54 | ToggleTrigger, 55 | } from '@alist/antd' 56 | import'antd/dist/antd.css' 57 | 58 | const actions = createListActions() 59 | const App = () => { 60 | return
61 | 68 | null} 71 | expandedRowRender={(record) => (record.label + '-' + record.value)} 72 | > 73 | 74 | 75 | { 76 | return 77 | }} /> 78 |
79 |
80 |
81 | } 82 | 83 | ReactDOM.render(, document.getElementById('root')) 84 | ``` -------------------------------------------------------------------------------- /docs/demo/fields.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/alist/d03650f3a7cdfb4c4ad71142d10d79d8ddfd3bf4/docs/demo/fields.md -------------------------------------------------------------------------------- /docs/demo/formily.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/alist/d03650f3a7cdfb4c4ad71142d10d79d8ddfd3bf4/docs/demo/formily.md -------------------------------------------------------------------------------- /docs/demo/hooks.md: -------------------------------------------------------------------------------- 1 | ## 与Formily配合 2 | 3 | 4 | ```jsx 5 | import React from 'react' 6 | import { useAList } from '@alist/next' 7 | import { Table, Pagination } from '@alifd/next' 8 | import '@alifd/next/dist/next.css' 9 | 10 | const App = () => { 11 | const { actions, table, pagiantion } = useAList({ 12 | dataSource: [ 13 | { id: '1', label: 'label-a', value: 'a' }, 14 | { id: '2', label: 'label-b', value: 'b' } 15 | ] 16 | }) 17 | 18 | return
19 | 20 | 21 | 22 |
23 | 24 |
25 | } 26 | 27 | ReactDOM.render(, document.getElementById('root')) 28 | ``` -------------------------------------------------------------------------------- /docs/demo/practise.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/alist/d03650f3a7cdfb4c4ad71142d10d79d8ddfd3bf4/docs/demo/practise.md -------------------------------------------------------------------------------- /docs/demo/selection.md: -------------------------------------------------------------------------------- 1 | # 设置及消费筛选状态 2 | 3 | ## 启用筛选状态 4 | 5 | 通过 [setRowSelection](#setRowSelection) 快速启用 6 | 7 | ```tsx 8 | 9 | const actions = createListActions(); 10 | 11 | const App = () => { 12 | 13 | useEffect(() => { 14 | actions.setRowSelection(); // 启用筛选项 15 | }, []); 16 | } 17 | ``` 18 | 19 | ## 消费筛选状态 20 | 21 | 通过 [Selection](#Selection) 消费筛选状态 22 | 23 | > 需要注意的是当没有启用筛选状态时,第一个参数为 `null`,需要做兼容处理。 24 | 25 | 26 | 27 | ```tsx 28 | 29 | import { Selection } from '@alist/antd' 30 | 31 | ``` 32 | 33 | ## 消费筛选状态例子(Ant-Design) 34 | 35 | ```jsx 36 | import ReactDOM from 'react-dom' 37 | import { Button } from 'antd' 38 | import React, { useEffect } from 'react' 39 | import { ListLifeCycleTypes, createListActions, Selection, List, Table, Pagination, Filter, Layout, Search, Clear } from '@alist/antd' 40 | import'antd/dist/antd.css' 41 | 42 | const actions = createListActions() 43 | 44 | const App = props => { 45 | const { children, ...others } = props 46 | const url = 'https://alist-wiki.oss-cn-beijing.aliyuncs.com/data.json' 47 | 48 | return ( 49 | { 53 | $(ListLifeCycleTypes.ON_LIST_MOUNTED).subscribe((payload) => { 54 | actions.setRowSelection() // 启用筛选模式 55 | }); 56 | $(ListLifeCycleTypes.ON_LIST_SELECTION_REFRESH).subscribe((payload) => { 57 | console.log('ON_LIST_SELECTION_REFRESH', payload) 58 | }); 59 | $(ListLifeCycleTypes.ON_LIST_SELECT).subscribe((payload) => { 60 | console.log('ON_LIST_SELECT', payload) 61 | }); 62 | $(ListLifeCycleTypes.ON_LIST_SELECT_CHANGE).subscribe((payload) => { 63 | console.log('ON_LIST_SELECT_CHANGE', payload) 64 | }); 65 | }} 66 | > 67 | 68 | {(opts, list) => { 69 | const { allIds, ids, selectedAll, selectedNone, dataSource } = opts || {}; 70 | return
已选中 {(ids || []).length} 条结果
71 | }} 72 |
73 | 74 | {/*
*/} 75 | 76 | 77 |
78 |
79 | ) 80 | } 81 | 82 | ReactDOM.render(, document.getElementById('root')) 83 | ``` 84 | -------------------------------------------------------------------------------- /docs/demo/sorterTable.md: -------------------------------------------------------------------------------- 1 | ## Table 区域支持排序 2 | 3 | ## Fusion-Next 4 | 5 | 通过 `Sorter` 可以快速实现 6 | 7 | ```jsx 8 | import React from 'react' 9 | import { List, Table, Sorter, ListLifeCycleTypes } from '@alist/next' 10 | import '@alifd/next/dist/next.css' 11 | 12 | const App = () => { 13 | return ( 14 |
15 | { 17 | $(ListLifeCycleTypes.ON_LIST_SORT).subscribe(payload => { 18 | console.log('ON_LIST_SORT', payload) 19 | }) 20 | }} 21 | dataSource={[ 22 | { id: '1', label: 'label-a', value: 'a' }, 23 | { id: '2', label: 'label-b', value: 'b' } 24 | ]} 25 | > 26 | 27 | 28 | 29 |
30 |
31 |
32 | ) 33 | } 34 | 35 | ReactDOM.render(, document.getElementById('root')) 36 | ``` 37 | 38 | 多表头结合排序 39 | 40 | ```jsx 41 | import React from 'react' 42 | import { List, Table, Sorter, ListLifeCycleTypes } from '@alist/next' 43 | import '@alifd/next/dist/next.css' 44 | 45 | const App = () => { 46 | return ( 47 |
48 | { 50 | $(ListLifeCycleTypes.ON_LIST_SORT).subscribe(payload => { 51 | console.log('ON_LIST_SORT', payload) 52 | }) 53 | }} 54 | dataSource={[ 55 | { id: '1', label: 'label-a', value: 'a', time: 'x' }, 56 | { id: '2', label: 'label-b', value: 'b', time: 'y' } 57 | ]} 58 | > 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 71 |
72 |
73 |
74 | ) 75 | } 76 | 77 | ReactDOM.render(, document.getElementById('root')) 78 | ``` 79 | 80 | ## Ant-Design 81 | 82 | 通过 `Sorter` 可以快速实现 83 | 84 | ```jsx 85 | import React from 'react' 86 | import { 87 | List, 88 | Table, 89 | Pagination, 90 | Filter, 91 | Layout, 92 | Search, 93 | Clear, 94 | createListActions, 95 | ListLifeCycleTypes, 96 | Sorter 97 | } from '@alist/antd' 98 | import 'antd/dist/antd.css' 99 | 100 | const actions = createListActions() 101 | const App = () => { 102 | return ( 103 |
104 | { 106 | $(ListLifeCycleTypes.ON_LIST_SORT).subscribe(payload => { 107 | console.log('ON_LIST_SORT', payload) 108 | }) 109 | }} 110 | actions={actions} 111 | dataSource={[ 112 | { id: '1', label: 'label-a', value: 'a' }, 113 | { id: '2', label: 'label-b', value: 'b' } 114 | ]} 115 | > 116 | 117 | 118 | 119 |
120 |
121 |
122 | ) 123 | } 124 | 125 | ReactDOM.render(, document.getElementById('root')) 126 | ``` 127 | -------------------------------------------------------------------------------- /docs/demo/syncParams.md: -------------------------------------------------------------------------------- 1 | # 同步URL参数 2 | 3 | 下面例子中,`username` 与URL参数绑定,初始化设置 `params` 会设置搜索字段初始值。随着字段变化,也会影响URL参数。 4 | 5 | ```jsx 6 | import React, { useEffect } from 'react' 7 | import { 8 | List, Table, Pagination, Filter, 9 | createListActions, Consumer, 10 | } from '@alist/antd' 11 | import'antd/dist/antd.css' 12 | 13 | const actions = createListActions() 14 | const App = () => { 15 | const url = 'https://alist-wiki.oss-cn-beijing.aliyuncs.com/data.json' 16 | 17 | return
18 | 24 | 25 | 26 | 27 | 28 | 29 | {() => { 30 | const p = new URLSearchParams(location.search) 31 | p.delete('path') 32 | return
url params: {p.toString()}
33 | }} 34 |
35 |
36 |
37 | } 38 | 39 | ReactDOM.render(, document.getElementById('root')) 40 | ``` -------------------------------------------------------------------------------- /docs/formily/tableSort.md: -------------------------------------------------------------------------------- 1 | ## Table 排序(Ant-Design) 2 | 3 | ```jsx 4 | import React, { useRef, useContext } from 'react' 5 | import { 6 | List, 7 | Table, 8 | Pagination, 9 | createListActions, 10 | Search 11 | } from '@alist/antd-components' 12 | import { 13 | SchemaForm, 14 | SchemaMarkupField as Field, 15 | Submit, 16 | createFormActions 17 | } from '@formily/antd' 18 | import { FormMegaLayout, Input } from '@formily/antd-components' 19 | import Printer from '@formily/printer' 20 | import 'antd/dist/antd.css' 21 | 22 | const listActions = createListActions() 23 | const actions = createFormActions() 24 | const App = () => { 25 | return ( 26 |
27 | 28 | 29 | 36 | 37 | 38 | 39 |
40 | 41 |
42 |
43 |
44 |
45 | ) 46 | } 47 | 48 | ReactDOM.render(, document.getElementById('root')) 49 | ``` 50 | 51 | ## Table 排序(Fusion-Design) 52 | 53 | ```jsx 54 | import React, { useRef, useContext } from 'react' 55 | import { 56 | List, 57 | Table, 58 | Pagination, 59 | createListActions, 60 | Search 61 | } from '@alist/next-components' 62 | import { 63 | SchemaForm, 64 | SchemaMarkupField as Field, 65 | Submit, 66 | createFormActions 67 | } from '@formily/next' 68 | import { FormMegaLayout, Input } from '@formily/next-components' 69 | import Printer from '@formily/printer' 70 | import '@alifd/next/dist/next.css' 71 | 72 | const listActions = createListActions() 73 | const actions = createFormActions() 74 | const App = () => { 75 | return ( 76 |
77 | 78 | 79 | 86 | 87 | 88 | 89 |
90 | 91 |
92 |
93 |
94 |
95 | ) 96 | } 97 | 98 | ReactDOM.render(, document.getElementById('root')) 99 | ``` 100 | -------------------------------------------------------------------------------- /docs/home.less: -------------------------------------------------------------------------------- 1 | .home-page { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | height: 80vh; 6 | 7 | .home-title { 8 | font-family: ArialRoundedMTBold; 9 | width: 100%; 10 | padding: 36px 0; 11 | color: #6857F6; 12 | letter-spacing: 1px; 13 | text-align: center; 14 | font-size: 120px; 15 | } 16 | } -------------------------------------------------------------------------------- /docs/home.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './home.less' 3 | 4 | export default () => { 5 | return ( 6 |
7 |
AList
8 |
9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /docs/iframe.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | AList 7 | 21 | 22 | 42 | 43 | 67 | 68 | 69 | 70 |
71 | 72 | 73 | 74 |
75 | 82 | 85 | 88 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | AList 7 | 21 | 22 | 42 | 43 | 67 | 68 | 69 | 70 |
71 | 72 | 73 | 74 |
75 | 82 | 85 | 88 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /docs/interface/actions.md: -------------------------------------------------------------------------------- 1 | ## 理解Actions/Effects 2 | 3 | `AList` 将所有列表相关的操作都抽象到 `ListActions`,我们可以通过 `createListActions` 获取它。 4 | 5 | 通过使用它的API实现各种列表操作,如刷新,重置,清空等操作, 具体可以查看 [API列表](#API列表)。 6 | 7 | ## 使用 8 | 9 | ```tsx 10 | 11 | import { List, createListActions, ListLifeCycleTypes } from '@alist/antd' 12 | const actions = createListActions() 13 | 14 | const APP = () => { 15 | return { 18 | $(ListLifeCycleTypes.ON_LIST_FILTER_ITEM_CHANGE).subscribe((state) => { 19 | if (state.name === 'username') { 20 | actions.refresh() // 搜索区域用户名字段变化时,刷新列表 21 | } 22 | }) 23 | }} 24 | > 25 | ... 26 | 27 | } 28 | ``` 29 | 30 | ## API列表 31 | 32 | 所有 `actions` 的API如下: 33 | 34 | | 方法名 | 描述 | 入参 | 结果 | 35 | |:----------|:---------------------------------|:--------------------|:--------------------| 36 | | refresh |刷新列表,会触发请求 | | | 37 | | clear |清空操作 | | | 38 | | reset |重置 | | | 39 | | setCurrentPage |分页跳转 | | | 40 | | setPageSize |设置分页大小 | | | 41 | | setLoading |设置loading状态 | boolean | | 42 | | getLoading |获取loading状态 | | boolean | 43 | | setUrl |动态切换请求url | string | | 44 | | setQuery |动态切换请求query | IQuery | | 45 | | getParams |获取url与搜索区域关联参数 | | | 46 | | setParams |设置url与搜索区域关联参数 | | | 47 | | getPageData |获取分页数据 | | | 48 | | setPageData |设置分页数据 | | | 49 | | getExpandStatus |获取展开收起状态 | | | 50 | | toggleExpandStatus |设置展开收起状态 | | | 51 | | getFilterData |获取搜索区域数据 | | | 52 | | setFilterData |设置搜索区域数据 | | | 53 | | getDataSource |获取dataSource | | | 54 | | setDataSource |动态更新dataSource | | | 55 | | getSelectionConfig |获取筛选配置 | | | 56 | | setSelectionConfig |设置筛选配置 | | | 57 | | disableSelectionConfig |禁用是筛选 | | | 58 | | getSelections |获取当前筛选项 | | | 59 | | getSortConfig |获取排序配置 | | | 60 | | setSortConfig |设置排序配置 | | | 61 | | getMultipleData |获取多实例数据 | | | 62 | | setMultipleData |设置多实例数据 | | | 63 | | setMultiplePageSize |设置多实例分页数据 | | | 64 | | getFilterProps |获取搜索区域props | | | 65 | | getFilterInstance |获取搜索区域实例 | | | 66 | | getTableProps |获取表格区域属性 | | | 67 | | setTableProps |设置表格区域属性,会触发重绘 | | | 68 | | setPaginationDataSource |设置分页后数据 | | | 69 | | getPaginationDataSource |获取分页后数据 | | | 70 | 71 | -------------------------------------------------------------------------------- /docs/interface/communicate.md: -------------------------------------------------------------------------------- 1 | ## 组件间通信机制 2 | 3 | 在 `List` 组件下,会根据请求结果分别控制 `Table`, `Pagination`, `Filter` 以及整体的 `loading` 整体控制等。 4 | 5 | ## 请求与组件默认处理机制 6 | 7 | 举例说明,当请求返回值为以下数据时: 8 | 9 | ```json 10 | { 11 | "dataList": [], 12 | "pageSize": 10, 13 | "currentPage": 2, 14 | "total": 22 15 | } 16 | ``` 17 | 18 | `AList` 会自动将请求的值传入到对应的组件,如下所示: 19 | 20 | ```tsx 21 | 22 | 23 | 24 | 25 | ``` 26 | 27 | ## 消费列表数据 28 | 29 | 通过 `` 可以消费列表的状态 30 | 31 | ```tsx 32 | 33 |
34 | 35 | 36 | {list => { 37 | const { currentPage, pageSize } = list.getPageData() 38 | const filterData = list.getFilterData() 39 | const paginationedData = list.getPaginationDataSource() 40 | const originData = list.getDataSource() 41 | return ( 42 |
43 | dataSource: {paginationedData.length}
44 | age: {filterData.age}
45 | username: {filterData.username}
46 | currentPage:{currentPage}
47 | pageSize:{pageSize}
48 |
49 | ) 50 | }} 51 |
52 | 53 | `` -------------------------------------------------------------------------------- /docs/interface/fields.md: -------------------------------------------------------------------------------- 1 | # 搜索区域 - 组件 2 | 3 | 搜索区域基于[Formily](https://github.com/alibaba/uform)实现,`AList`的用法是基于 `JSON Schema` 实现。 4 | 5 | ## 基本使用 6 | 7 | ```tsx 8 | import { List, Filter, Layout } from '@alist/next' 9 | 10 | const App = () => { 11 | return 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | } 20 | ``` 21 | 22 | ## 内置组件 23 | 24 | 下面表格是内置表单组件的类型和对应组件关系表 25 | 26 | | type类型 | 对应组件 | 描述 | 27 | |:--------------|:----------------------------------|:----------------------| 28 | | input | Input | 输入框 | 29 | | select | Select | 下拉框 | 30 | | checkbox | CheckboxGroup | Checkbox | 31 | | radio | RadioGroup | Radio | 32 | | boolean | Switch | 开关组件 | 33 | | date | DatePicker | 日期选择器 | 34 | | time | TimePicker | 时间选择器 | 35 | | daterange | DatePicker x 2 | 范围日期选择器 | 36 | | rating | Rating | 评价组件 | 37 | 38 | ## Filter.Item属性 39 | 40 | | 属性名 | 描述 | 类型 | 默认值 | 41 | |:--------------|:----------------------------------|:----------------------|:---------| 42 | | title | 表单组件Label | string | "" | 43 | | name | 表单组件key | string | "" | 44 | | enum | 相当于传入组件的dataSource | Array | []s | 45 | | x-component-props | 透传到表单组件的属性 | Object | {} | 46 | | required | 是否必填,会触发校验 | boolean | false | 47 | | rules | 校验规则,请参考[校验规则文档](https://uform-next.netlify.com/#/MpI2Ij/rRCmCPsOHO) | ValidateDescription | null | 48 | | span | 组件跨列数 | number | 1 | 49 | | hasBorder | 是否有边框 | boolean | true | 50 | 51 | ## 使用实例 52 | 53 | ```tsx 54 | 59 | 60 | 63 | ``` 64 | 65 | ## 自定义组件 66 | 67 | 这一块的文档和 `Formily` 完全一致,请参考[注册自定义组件](https://uformjs.org/#/97UlUl/JNcMcBuYuy) 68 | 69 | ```tsx 70 | import { registerFormField, connect } from '@alife/scm-list' 71 | 72 | registerFormField( 73 | "custom", 74 | connect({ 75 | getProps: (props, fieldProps) => { 76 | // 对传入组件的props进行自定义 77 | }, 78 | getComponent: (Component, props, fieldProps) => { 79 | // 做预览态的操作可以在这里完成 80 | } 81 | })(Input) 82 | ); 83 | 84 | ``` -------------------------------------------------------------------------------- /docs/interface/lifecycle.md: -------------------------------------------------------------------------------- 1 | ## 生命周期 2 | 3 | ## 引入 4 | 5 | 以 `Ant-Design` 举例,`Fusion-Next` 可类比。 6 | 7 | 列表中发生任何事情时都会触发对应的生命周期,开发者可以通过消费这些生命周期执行副作用。 8 | 9 | ```tsx 10 | import { List, createListActions, ListLifeCycleTypes } from '@alist/antd' 11 | const App = () => { 12 | const actions = createListActions() 13 | return { 15 | $(ListLifeCycleTypes.ON_LIST_WILL_INIT).subscribe(() => { 16 | console.log('list will init') 17 | }) 18 | $(ListLifeCycleTypes.ON_LIST_INIT).subscribe(() => { 19 | console.log('list init') 20 | }) 21 | $(ListLifeCycleTypes.ON_LIST_BEFORE_QUERY).subscribe((queryData) => { 22 | console.log('list before query', queryData) 23 | }) 24 | 25 | $(ListLifeCycleTypes.ON_LIST_AFTER_QUERY).subscribe((resp) => { 26 | console.log('list after query', resp) 27 | }) 28 | }} 29 | actions={actions} 30 | > 31 | ... 32 | 33 | } 34 | ``` 35 | 36 | ## 类型列举 37 | 38 | 所有生命周期汇总如下,所有变量名推荐使用 `ListLifeCycleTypes` 使用: 39 | 40 | | 变量名 | 变量值 | 描述 | 41 | |:----------|:---------------------------------|:--------------------| 42 | | ON_LIST_WILL_INIT |onListWillInit | 列表即将初始化 | 43 | | ON_LIST_INIT |onListInit | 列表初始化完成 | 44 | | ON_LIST_MOUNTED |onListMounted | 列表挂载完成 | 45 | | ON_LIST_INIT_PARAMS_SET |onListInitParamsSet | 列表初始化URL参数关联设置 | 46 | | ON_LIST_PARAMS_CHANGE |onListParamsChange | 列表关联URL参数发生变化 | 47 | | WILL_LIST_UPDATE |willListUpdate | 列表即将更新 | 48 | | DID_LIST_UPDATE |didListUpdate | 列表更新完成 | 49 | | ON_LIST_FILTER_ITEM_CHANGE |onListFilterItemChange | 搜索区域字段发生变化 | 50 | | ON_LIST_FILTER_ITEM_EXPAND |onListFilterItemExpand | 搜索区域展开触发 | 51 | | ON_LIST_FILTER_ITEM_COLLAPSE |onListFilterItemCollapse | 搜索区域展开收起 | 52 | | ON_LIST_VALIDATE_START |onListValidateStart | 搜索区域校验开始 | 53 | | ON_LIST_VALIDATE_END |onListValidateEnd | 搜索区域校验结束 | 54 | | ON_LIST_CLEAR |onListClear | 搜索区域点击清空 | 55 | | ON_LIST_RESET |onListReset | 搜索区域点击重置 | 56 | | ON_LIST_BEFORE_QUERY |onListBeforeQuery | 列表请求前 | 57 | | ON_LIST_AFTER_QUERY |onListAfterQuery | 列表请求后 | 58 | | ON_LIST_ERROR |onListError | 列表请求失败 | 59 | | ON_LIST_EMPTY |onListEmpty | 列表请求返回空数据 | 60 | | ON_LIST_SELECT |onListSelect | 表格触发选择某一项 | 61 | | ON_LIST_SELECT_ALL |onListSelectAll | 表格触发选择全部 | 62 | | ON_LIST_SELECT_CHANGE |onListSelectChange | 表格触发选择改变 | 63 | | ON_LIST_DATASOURCE_FILTER |onListDatasourceFilter | 表格触发过滤 | 64 | | ON_LIST_DATASOURCE_SORT |onListDatasourceSort | 表格触发选择排序 | 65 | | ON_LIST_SORT |onListSort | 等价于`ON_LIST_DATASOURCE_SORT` | 66 | | ON_LIST_FILTER |onListFilter | 等价于`ON_LIST_DATASOURCE_FILTER` | 67 | | ON_LIST_MULTIPLE_REFRESH |onListMultipleRefresh | 多实例列表触发重绘 | 68 | | ON_LIST_VALIDATE_CONFIG_REFRESH |onListValidateConfigRefresh | 搜索区域校验规则发生变化 | 69 | | ON_LIST_FILTER_REFRESH |onListFilterRefresh | 搜索区域触发重绘 | 70 | | ON_LIST_PAGINATION_REFRESH |onListPaginationRefresh | 分页区域触发重绘 | 71 | | ON_LIST_TABLE_REFRESH |onListTableRefresh | 表格区域触发重绘 | 72 | | ON_LIST_SELECTION_REFRESH |onListSelectionRefresh | 表格筛选项发生重绘 | 73 | | ON_LIST_CONSUMER_REFRESH |onListConsumerRefresh | 自定义消费组件触发重绘 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /docs/interface/multiple.md: -------------------------------------------------------------------------------- 1 | ## 多列表实例模式 2 | 3 | ## 控制接口返回数据控制 4 | 5 | 通过控制返回的数据结构中有 `multipleData` 激活多实例模式,并且配合 `MultipleProvider` 进行本地分页处理。 6 | 7 | 请查看源码中的 `formatAfter` 部分 8 | 9 | > MultipleProvider 默认分页大小为10 10 | 11 | ```jsx 12 | import React, { useEffect } from 'react' 13 | import { 14 | List, Table, Pagination, MultipleProvider, 15 | createListActions, 16 | } from '@alist/antd' 17 | import'antd/dist/antd.css' 18 | 19 | const actions = createListActions() 20 | const App = () => { 21 | const url = 'https://alist-wiki.oss-cn-beijing.aliyuncs.com/data.json' 22 | 23 | return
24 | { 25 | const { dataList, ...others } = resp; 26 | const multipleData = { 27 | a1: [], 28 | a2: [], 29 | }; 30 | dataList.forEach((item, idx) => { 31 | if (idx < 6) { 32 | multipleData.a1.push(item) 33 | } else { 34 | multipleData.a2.push(item) 35 | } 36 | }) 37 | 38 | return { 39 | ...resp, 40 | multipleData, 41 | }; 42 | }}> 43 |

List 1 pageSize: 5

44 | 45 |
46 | 47 | 48 |
49 | 50 | 51 | 52 |

List 2 pageSize: 3

53 | 54 | 55 | 56 | 57 |
58 | 59 |
60 |
61 | 62 | } 63 | 64 | ReactDOM.render(, document.getElementById('root')) 65 | ``` 66 | 67 | ## 手动设置 68 | 69 | 使用 `MultipleProvider` 配合 `setMultipleData` 将同一个接口的数据让多个列表消费。 70 | 71 | 请查看源码中的 `setMultipleData` 部分 72 | 73 | > MultipleProvider 默认分页大小为10 74 | 75 | ```jsx 76 | import React, { useEffect } from 'react' 77 | import { 78 | List, Table, Pagination, MultipleProvider, 79 | createListActions, 80 | } from '@alist/antd' 81 | import'antd/dist/antd.css' 82 | 83 | const getDataSource = (len) => { 84 | const dataSource = [] 85 | for ( let i = 0; i < len; i++ ) { 86 | dataSource.push({ label: `id: #${Math.random().toString(36).slice(-8)}`, value: i }) 87 | } 88 | 89 | return dataSource 90 | } 91 | 92 | const actions = createListActions() 93 | const App = () => { 94 | useEffect(() => { 95 | actions.setMultipleData({ 96 | a1: getDataSource(15), 97 | a2: getDataSource(15), 98 | }) 99 | }, []) 100 | 101 | return
102 |
同一份接口数据,多个列表实例消费
103 | 104 |

List 1 pageSize: 10

105 | 106 | 107 | 108 | 109 |
110 | 111 |
112 | 113 |

List 2 pageSize: 3

114 | 115 | 116 | 117 | 118 |
119 | 120 |
121 |
122 |
123 | } 124 | 125 | ReactDOM.render(, document.getElementById('root')) 126 | ``` -------------------------------------------------------------------------------- /docs/interface/query.md: -------------------------------------------------------------------------------- 1 | ## 标准请求格式 2 | 3 | ![standard-query-flow](https://img.alicdn.com/tfs/TB1.w1EwQL0gK0jSZFxXXXWHVXa-2002-290.png) 4 | 5 | 如图所示为一次请求的过程,请求的参数为 [IListQueryData](#IListQueryData),返回结果为 [IListResponse](#IListResponse)。 6 | 7 | #### IListQueryData 8 | 9 | | 参数 | 说明 | 类型 | 默认值 | 10 | |:----------|:---------------------------------|:--------------------|:--------------------| 11 | | sort |排序配置 | { [key: string]: 'desc' `|` 'asc' } | | 12 | | currentPage |当前页 | number | | 13 | | pageSize |分页大小 | number | 10 | 14 | | filterData |搜索区域表单values | {[key]: any} | {} | 15 | 16 | 17 | #### IListResponse 18 | 19 | | 参数 | 说明 | 类型 | 默认值 | 20 | |:----------|:---------------------------------|:--------------------|:--------------------| 21 | | dataList |数据列表,对应Table的`dataSource` | any[] | | 22 | | multipleData |如果有此字段,即为多实例模式 | { string: any[] } | | 23 | | total |总条目数 | number | | 24 | | pageSize |分页大小 | number | 10 | 25 | | currentPage |当前页数 | number | | 26 | | totalPages |总页面数 | number | | 27 | 28 | 29 | ## 修改请求流程 30 | 31 | 只需要满足入参及出参均符合标准,`AList` 会负责相关数据和渲染的工作,用户能够通过以下几种方式影响流程: 32 | 33 | > 更多示例可以查看 [定制请求](#) 34 | 35 | * `formatFilter` 修改搜索数据,即 `filterData` 36 | * `formatBefore` 修改请求前的数据,即 `IListQueryData` 37 | * `formatAfter` 修改请求返回后的数据,返回值 **必须** 为 `IListResponse`。 38 | * `query` 直接代理整个网络请求的过程,输入为 `IListQueryData`, 返回值 **必须** 为 `IListResponse`。 39 | 40 | ## formatFilter 41 | 42 | 常用于根据搜索区域组件值做请求前的调整, 例子:根据code补齐label数据 43 | 44 | ```tsx 45 | { 46 | const [val, objValue] = list.getFieldState('gender', state => state.values) 47 | return { 48 | ...filterData, 49 | genderLabeel: objValue.label 50 | } 51 | }}> 52 | 53 | 57 | 58 | 59 | ``` 60 | 61 | ## formatBefore 62 | 63 | 最常用的方法,用于调整请求前的数据调整,入参类型为 [IListQueryData](#IListQueryData)。 例子:修改请求数据,打平放入json字段中 64 | 65 | ```tsx 66 | { 67 | const { filterData, currentPage, pageSize, sort } = queryData 68 | return { 69 | json: { 70 | ...filterData, 71 | currentPage, 72 | pageSize, 73 | sort 74 | } 75 | } 76 | }}> 77 | ... 78 | 79 | ``` 80 | 81 | ## formatAfter 82 | 83 | 用于修改请求返回后的数据, 必须保证返回的数据格式为 [IListResponse](#IListResponse) 84 | 85 | ```tsx 86 | { 87 | const { dataList, multipleData, total, totalPages, currentPage, pageSzie } = resp; 88 | return resp; 89 | }}> 90 | ... 91 | 92 | ``` 93 | 94 | 95 | ## query 96 | 97 | 直接替换掉默认的请求过程,发生于 **formatBefore** 之后,**formatAfter** 之前。 98 | 99 | ```tsx 100 | { 101 | const resp = await yourFetch('someApiUrl', { 102 | json: queryData 103 | }) 104 | return resp; 105 | }}> 106 | ... 107 | 108 | ``` -------------------------------------------------------------------------------- /docs/quickStart.md: -------------------------------------------------------------------------------- 1 | ## 安装 2 | 3 | 根据您正在使用的UI库做选择,目前支持 `Fusion-Next` 及 4 | `Ant-Design`。 5 | 6 | #### Ant-Design 7 | 8 | ```shell 9 | $ npm install @alist/antd 10 | ``` 11 | 12 | #### Fusion-Next 13 | 14 | ```shell 15 | $ npm install @alist/next 16 | ``` 17 | 18 | ## 引入及使用 19 | 20 | 项目以 `Ant-Deisgn` 为例,`Fusion-Next` 同理。 21 | 22 | > 查看源码可以点击右下角到 `codesandbox查看` 或者 `本地查看`。 23 | 24 | ```jsx 25 | import React from 'react' 26 | import { 27 | List, Table, Pagination, 28 | Filter, 29 | Layout, 30 | Search, 31 | Clear, 32 | createListActions, 33 | ListLifeCycleTypes, 34 | } from '@alist/antd' 35 | import'antd/dist/antd.css' 36 | 37 | const actions = createListActions() 38 | const App = () => { 39 | const url = 'https://alist-wiki.oss-cn-beijing.aliyuncs.com/data.json' 40 | 41 | return
42 |
打开控制台查看Network发起的请求
43 | 44 | { 49 | // triggered when filterItem change 50 | $(ListLifeCycleTypes.ON_LIST_FILTER_ITEM_CHANGE).subscribe((state) => { 51 | console.log(state) 52 | }); 53 | }} 54 | > 55 | 56 | 57 | 58 | 59 | 60 | 61 | 搜索 62 | 重置 63 | 64 | 65 | 66 | 67 | 68 |
69 | 70 |
71 |
72 | } 73 | 74 | ReactDOM.render(, document.getElementById('root')) 75 | ``` -------------------------------------------------------------------------------- /docs/schema/index.md: -------------------------------------------------------------------------------- 1 | ## 理解Schema 2 | 3 | ## 结构定义 4 | 5 | `AList` 可以通过 `JSON-Schema` 来渲染列表,以下是规范及定义: 6 | 7 | | 变量名 | 描述 | 类型 | 8 | |:----------|:---------------------------------|:--------------------| 9 | | componentsTree | 组件树 | Array | 10 | | i18n | 国际化文案集 | {[i18n-key]: { [string]: string } } | 11 | 12 | * Component 13 | 14 | | 变量名 | 描述 | 类型 | 15 | |:----------|:---------------------------------|:--------------------| 16 | | componentName | 组件名称 | string | 17 | | props | 组件属性 | { [string]: any } | 18 | | children | 组件内容 | Array | 19 | 20 | 1. **children** 下的字符串内容,及 **props** 内所有带有 `$i18n.` 的文字,都会尝试去 `i18n` 配置内查找并替换。 21 | 2. **props** 内所有的属性,带有 `$func.` 的属性,都会尝试从 `` 的 `funcRegistry` 内查找并替换。 22 | 23 | ## 结构示例 24 | 25 | ```json 26 | { 27 | "componentsTree": [ 28 | { 29 | "componentName": "div", 30 | "props": { 31 | "onClick": "$func.onClick", 32 | }, 33 | "children": [ 34 | "hello", 35 | { 36 | "componentName": "span", 37 | "children": ["$i18n.china"] 38 | } 39 | ] 40 | } 41 | ], 42 | "i18n": { 43 | "zh-CN": { 44 | "hello": "你好", 45 | "china": "中国" 46 | }, 47 | "en-US": { 48 | "hello": "Hello", 49 | "china": "China" 50 | } 51 | } 52 | } 53 | ``` 54 | 55 | ## 用法 56 | 57 | 通过 `` 来渲染 **schema** 模式下的 **AList**,核心属性如下: 58 | 59 | | 变量名 | 描述 | 类型 | 60 | |:----------|:---------------------------------|:--------------------| 61 | | schema | 描述AList的schema | IListSchema | 62 | | funcRegistry | 自定义注册方法列表 | { [string]: function } | 63 | | componentsRegistry | 自定义注册组件类型 | { [string]: ReactElement } | 64 | 65 | ```tsx 66 | import { SchemaList } from '@alist/antd' 67 | 68 | const App = () => { 69 | return {} 73 | }} 74 | componentsRegistry={{ // 自定义注册组件类型 75 | CustomComponent: (props) =>
{props.children}
76 | }} 77 | /> 78 | } 79 | ``` 80 | 81 | ## 预注册 82 | 83 | 还可以通过预注册的方法来提前注册方法及组件类型 84 | 85 | ```tsx 86 | import { registerListFuncs, registerListComponent } from '@alist/antd' 87 | 88 | // 预注册moment处理函数 89 | registerListFuncs({ 90 | moment: (val, format) => moment(val).format(format || 'YYYY-MM-DD HH:mm:ss') 91 | }) 92 | 93 | // 预注册组件类型 94 | registerListComponent({ 95 | CustomComponent: (props) =>
{props.children}
96 | }) 97 | 98 | ``` 99 | 100 | ## 内置组件类型 101 | 102 | | 变量名 | 描述 | 103 | |:----------|:---------------------------------| 104 | | List | AList容器 | 105 | | Table | Table组件 | 106 | | Table.Column | Table子组件 | 107 | | Pagination | Pagination组件 | 108 | | Filter | Filter组件 | 109 | | Filter.Item | Filter字段 | 110 | | Consumer | Consumer组件 | 111 | | div | html组件 | 112 | | span | html组件 | 113 | | img | html组件 | 114 | | a | html组件 | 115 | | a | html组件 | 116 | | b | html组件 | 117 | | i | html组件 | 118 | | p | html组件 | 119 | | hr | html组件 | 120 | | br | html组件 | 121 | 122 | -------------------------------------------------------------------------------- /docs/test.md: -------------------------------------------------------------------------------- 1 | ```jsx 2 | import React from 'react' 3 | import { 4 | List, Table, Pagination, 5 | Filter, 6 | Layout, 7 | Search, 8 | Clear, 9 | createListActions, 10 | ListLifeCycleTypes, 11 | } from '@alist/antd' 12 | import'antd/dist/antd.css' 13 | 14 | const actions = createListActions() 15 | const App = () => { 16 | const url = 'https://alist-wiki.oss-cn-beijing.aliyuncs.com/data.json' 17 | 18 | return
19 |
打开控制台查看Network发起的请求
20 | 21 | 22 | 23 | 24 | 25 | 32 | 39 | 46 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 搜索 62 | 重置 63 | 64 | 65 | 66 |
67 | } 68 | 69 | ReactDOM.render(, document.getElementById('root')) 70 | ``` 71 | 72 | ```jsx 73 | import React from 'react' 74 | import { 75 | List, Table, 76 | ToggleTrigger, 77 | } from '@alist/next' 78 | import '@alifd/next/dist/next.css' 79 | 80 | const App = () => { 81 | return
82 | 88 | (record.label + '-' + record.value)} 91 | hasExpandedRowCtrl={false} 92 | primaryKey="value" 93 | > 94 | 95 | 96 | { 97 | return 98 | }} /> 99 |
100 |
101 |
102 | } 103 | 104 | ReactDOM.render(, document.getElementById('root')) 105 | ``` -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./scripts/jest.base') 2 | // jest.config.js 3 | 4 | // Note: If you are using babel version 7 you have to install babel-jest with 5 | // yarn add --dev babel-jest 'babel-core@^7.0.0-bridge' @babel/core 6 | 7 | module.exports = { 8 | ...baseConfig, 9 | rootDir: '.', 10 | projects: ['/pacakges/*/jest.config.js'] 11 | } 12 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "1.0.59", 6 | "command": { 7 | "bootstrap": { 8 | "npmClientArgs": [ 9 | "--no-package-lock" 10 | ] 11 | }, 12 | "publish": { 13 | "allowBranch": "master", 14 | "ignoreChanges": [ 15 | "*.md" 16 | ] 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "extends": [ 4 | "plugin:react/recommended", 5 | "plugin:@typescript-eslint/recommended", 6 | "prettier/@typescript-eslint", 7 | ], 8 | "env": { 9 | "browser": true, 10 | "es6": true, 11 | "jest": true, 12 | "commonjs": true 13 | }, 14 | "plugins": [ 15 | "@typescript-eslint", 16 | "react", 17 | "react-hooks", 18 | "prettier" 19 | ], 20 | "parserOptions": { 21 | "project": "./tsconfig.json", 22 | "sourceType": "module", 23 | "ecmaVersion": 6, 24 | "ecmaFeatures": { 25 | "jsx": true 26 | } 27 | }, 28 | "settings": { 29 | "react": { 30 | "version": "detect", 31 | } 32 | }, 33 | "rules": { 34 | "prettier/prettier": 2, 35 | // don't force es6 functions to include space before paren 36 | "space-before-function-paren": 0, 37 | "react/prop-types": 0, 38 | "react/no-find-dom-node": 0, 39 | "react/display-name": 0, 40 | // allow specifying true explicitly for boolean props 41 | "react/jsx-boolean-value": 0, 42 | "react-hooks/rules-of-hooks": 2, 43 | "react/no-did-update-set-state": 0, 44 | // maybe we should no-public 45 | "@typescript-eslint/explicit-member-accessibility": 0, 46 | "@typescript-eslint/interface-name-prefix": 0, 47 | "@typescript-eslint/no-explicit-any": 0, 48 | "@typescript-eslint/explicit-function-return-type": 0, 49 | "@typescript-eslint/no-parameter-properties": 0, 50 | "@typescript-eslint/array-type": 0, 51 | "@typescript-eslint/no-object-literal-type-assertion": 0, 52 | "@typescript-eslint/no-use-before-define": 0, 53 | "no-console": [ 54 | "error", 55 | { 56 | "allow": [ 57 | "warn", 58 | "error", 59 | "info" 60 | ] 61 | } 62 | ] 63 | } 64 | } -------------------------------------------------------------------------------- /packages/antd-components/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | build 4 | __tests__ -------------------------------------------------------------------------------- /packages/antd-components/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present, Alibaba Group Holding Limited. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /packages/antd-components/build.ts: -------------------------------------------------------------------------------- 1 | import { compile, getCompileConfig } from '../../scripts/build' 2 | import ts from 'typescript' 3 | import tsImportPluginFactory from 'ts-import-plugin' 4 | import glob from 'glob' 5 | import * as fs from 'fs-extra' 6 | 7 | const transformer = tsImportPluginFactory({ 8 | libraryName: 'antd', 9 | // style: 'css', 10 | }) 11 | 12 | function buildESM() { 13 | const { fileNames, options } = getCompileConfig(require.resolve('./tsconfig.json'), { 14 | outDir: './esm', 15 | module: ts.ModuleKind.ESNext 16 | }) 17 | compile(fileNames, options) 18 | console.log('esm build successfully') 19 | } 20 | 21 | const TEMP_OUT_DIR = './temp_esm' 22 | 23 | function buildTempESM() { 24 | const { fileNames, options } = getCompileConfig(require.resolve('./tsconfig.json'), { 25 | outDir: TEMP_OUT_DIR, 26 | module: ts.ModuleKind.ESNext 27 | }) 28 | compile(fileNames, options, { before: [transformer] }) 29 | 30 | console.log('temporary esm build successfully') 31 | } 32 | 33 | function clearTempESM() { 34 | fs.removeSync(TEMP_OUT_DIR) 35 | 36 | console.log('clear temporary esm build successfully') 37 | } 38 | 39 | function buildES5() { 40 | const rootNames = glob.sync(`${TEMP_OUT_DIR}/**/*.js`) 41 | compile(rootNames, { 42 | allowJs: true, 43 | esModuleInterop: true, 44 | moduleResolution: ts.ModuleResolutionKind.NodeJs, 45 | module: ts.ModuleKind.CommonJS, 46 | target: ts.ScriptTarget.ES5, 47 | outDir: './lib', 48 | declaration: false, 49 | }) 50 | console.log('es5 build successfully') 51 | } 52 | 53 | 54 | 55 | buildESM() 56 | 57 | buildTempESM() 58 | buildES5() 59 | clearTempESM() 60 | -------------------------------------------------------------------------------- /packages/antd-components/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@alist/antd-components", 3 | "version": "1.0.59", 4 | "main": "lib", 5 | "engines": { 6 | "npm": ">=3.0.0" 7 | }, 8 | "scripts": { 9 | "build": "ts-node --project ../../tsconfig.build.json build.ts" 10 | }, 11 | "stylePath": "style.js", 12 | "devDependencies": { 13 | "@types/classnames": "^2.2.9", 14 | "@types/styled-components": "^4.4.0", 15 | "antd": "4.x", 16 | "styled-components": "^4.4.1", 17 | "typescript": "^3.5.2" 18 | }, 19 | "types": "esm/index.d.ts", 20 | "peerDependencies": { 21 | "@babel/runtime": "^7.4.4", 22 | "@formily/antd": "^1.2.1", 23 | "antd": "4.x", 24 | "react": ">=16.8.0", 25 | "react-dom": ">=16.8.0", 26 | "react-eva": "^1.1.7", 27 | "styled-components": "^4.4.1" 28 | }, 29 | "dependencies": { 30 | "@alist/react": "^1.0.59", 31 | "@formily/antd": "^1.2.1" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "git@gitlab.alibaba-inc.com:a-lib/alist.git" 36 | }, 37 | "publishConfig": { 38 | "access": "public" 39 | }, 40 | "license": "MIT", 41 | "gitHead": "76f2ae551cdfe1a87e86f21b8647cfa46edc507a" 42 | } 43 | -------------------------------------------------------------------------------- /packages/antd-components/src/__tests__/index.spec.tsx: -------------------------------------------------------------------------------- 1 | describe('index',()=>{ 2 | test('todo', () => { 3 | 4 | }) 5 | }) -------------------------------------------------------------------------------- /packages/antd-components/src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import { createVirtualBox, FormButtonGroup } from '@formily/antd' 2 | 3 | const ButtonGroup = createVirtualBox('alist-btn-group', FormButtonGroup) 4 | 5 | export { 6 | ButtonGroup 7 | } -------------------------------------------------------------------------------- /packages/antd-components/src/components/Clear.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Consumer, ListLifeCycleTypes } from '@alist/react' 3 | import { createControllerBox } from '@formily/antd' 4 | import { Button } from 'antd' 5 | 6 | const InternalClear = (props) => { 7 | const { form, render, children, content, ...others } = props 8 | return 9 | {(list) => { 10 | if (list) { 11 | const { clear } = list 12 | if (typeof render === 'function') { 13 | return render(clear) 14 | } 15 | return 20 | } else { 21 | return 26 | } 27 | }} 28 | 29 | } 30 | 31 | const Clear = createControllerBox('alist-clear', (props) => { 32 | const { form, schema } = props 33 | const componentProps = schema.getExtendsComponentProps() 34 | return 35 | }) 36 | 37 | export { 38 | Clear, 39 | InternalClear, 40 | } -------------------------------------------------------------------------------- /packages/antd-components/src/components/Consumer.tsx: -------------------------------------------------------------------------------- 1 | import { Consumer as InternalConsumer } from '@alist/react' 2 | import { createVirtualBox } from '@formily/antd' 3 | 4 | const ListSpy = createVirtualBox('alist-consumer', InternalConsumer) 5 | 6 | export { 7 | ListSpy, 8 | ListSpy as ListConsumer, 9 | InternalConsumer as InternalListSpy, 10 | InternalConsumer, 11 | } -------------------------------------------------------------------------------- /packages/antd-components/src/components/ExpandContainer.tsx: -------------------------------------------------------------------------------- 1 | import { createExpandContainer } from '@alist/react' 2 | import { createControllerBox, InternalVirtualField } from '@formily/antd' 3 | 4 | const ExpandContainer = createExpandContainer({ VirtualField: InternalVirtualField, createControllerBox }) 5 | 6 | export { 7 | ExpandContainer, 8 | ExpandContainer as ListExpandContainer, 9 | }; -------------------------------------------------------------------------------- /packages/antd-components/src/components/ExpandTrigger.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ExpandTrigger } from '@alist/react' 3 | import { createVirtualBox, createControllerBox, FormSpy } from '@formily/antd' 4 | import { Button } from 'antd' 5 | 6 | const InternalExpandTrigger = (props) => { 7 | const { render, expandText, unExpandText, ...others } = props 8 | 9 | return 10 | {({ form }) => { 11 | return 12 | {({ toggleExpandStatus, expandStatus }) => { 13 | if (typeof render === 'function') { 14 | return render({ toggleExpandStatus, expandStatus }) 15 | } 16 | return 19 | }} 20 | 21 | }} 22 | 23 | } 24 | 25 | createVirtualBox('filter-expand-trigger', InternalExpandTrigger) 26 | const ListExpandTrigger = createControllerBox('alist-expand-trigger', (props) => { 27 | const { schema } = props 28 | const componentProps = schema.getExtendsComponentProps() 29 | return 30 | }) 31 | 32 | export { 33 | ListExpandTrigger, 34 | InternalExpandTrigger, 35 | } -------------------------------------------------------------------------------- /packages/antd-components/src/components/List.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useRef } from 'react'; 2 | import { ListProvider, ListContext } from '@alist/react' 3 | import { createControllerBox, useFormEffects } from '@formily/antd' 4 | import useAntdList from '../hooks/useAntdList'; 5 | 6 | const InternalList = (props) => { 7 | const opts = useAntdList(props) 8 | return 9 | } 10 | 11 | const ListFilterSpy = (props) => { 12 | const settingRef = useRef(null) 13 | 14 | const { form } = props 15 | const list = useContext(ListContext) 16 | const effects = list.getFilterEffects() 17 | if (!settingRef.current) { 18 | list.setFilterInstance(form) 19 | list.initSyncFilterData(true) 20 | settingRef.current = true 21 | } 22 | 23 | useFormEffects(effects) 24 | 25 | return null 26 | } 27 | 28 | 29 | const VirtualList = (props) => { 30 | const { form, children, schema } = props 31 | const componentProps = schema.getExtendsComponentProps() 32 | 33 | return 34 | 35 | {children} 36 | 37 | } 38 | const List = createControllerBox('alist', VirtualList) 39 | 40 | export { 41 | List, 42 | VirtualList, 43 | InternalList, 44 | } -------------------------------------------------------------------------------- /packages/antd-components/src/components/MultipleProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { MultipleProvider as InternalMultipleProvider } from '@alist/react' 3 | import { createControllerBox } from '@formily/antd' 4 | 5 | const MultipleProvider = createControllerBox('alist-multipleProvider', (props) => { 6 | const { form, schema, children } = props 7 | const componentProps = schema.getExtendsComponentProps() 8 | return 9 | {children} 10 | 11 | }) 12 | 13 | export { 14 | MultipleProvider, 15 | InternalMultipleProvider, 16 | } -------------------------------------------------------------------------------- /packages/antd-components/src/components/Pagination.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react' 2 | import { PaginationProvider } from '@alist/react' 3 | import { Pagination as AntdPagination } from 'antd' 4 | import { PaginationProps } from 'antd/lib/pagination/Pagination' 5 | import { createVirtualBox } from '@formily/antd' 6 | import styled from 'styled-components' 7 | 8 | const InternalPagination = styled(forwardRef((props, ref) => { 9 | return 10 | {(connectProps) => { 11 | const { currentPage, setCurrentPage, setPageSize, pageSize, ...other } = connectProps; 12 | return { 13 | setPageSize(pageSize); 14 | }} onPageSizeChange={setPageSize} {...other} {...props} /> 15 | }} 16 | 17 | }))` 18 | margin: 16px 0; 19 | text-align: ${(props) => (props.align || 'right')}; 20 | ` 21 | 22 | const Pagination = createVirtualBox('alist-pagination', InternalPagination) 23 | 24 | export { 25 | Pagination, 26 | InternalPagination, 27 | } -------------------------------------------------------------------------------- /packages/antd-components/src/components/Reset.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Consumer, ListLifeCycleTypes } from '@alist/react' 3 | import { createVirtualBox, createControllerBox } from '@formily/antd' 4 | import { Button } from 'antd' 5 | 6 | const InternalReset = (props) => { 7 | const { form, render, content, children, ...others } = props 8 | return 9 | {(list) => { 10 | if (list) { 11 | const { reset } = list 12 | if (typeof render === 'function') { 13 | return render(reset) 14 | } 15 | return 20 | } else { 21 | return 26 | } 27 | }} 28 | 29 | } 30 | 31 | createVirtualBox('reset', InternalReset) 32 | const Reset = createControllerBox('alist-reset', (props) => { 33 | const { form, schema } = props 34 | const componentProps = schema.getExtendsComponentProps() 35 | return 36 | }) 37 | 38 | export { 39 | InternalReset, 40 | Reset 41 | } -------------------------------------------------------------------------------- /packages/antd-components/src/components/Search.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Consumer, ListLifeCycleTypes } from '@alist/react' 3 | import { Button } from 'antd' 4 | import { createVirtualBox, createControllerBox } from '@formily/antd' 5 | 6 | const noop = () => {} 7 | const InternalSearch = (props) => { 8 | const { form, enableLoading, render, content, children, ...others } = props 9 | return { 12 | switch (action.type) { 13 | case ListLifeCycleTypes.ON_LIST_BEFORE_QUERY: 14 | return { 15 | ...state, 16 | loading: true 17 | } 18 | case ListLifeCycleTypes.ON_LIST_ERROR: 19 | case ListLifeCycleTypes.DID_LIST_UPDATE: 20 | return { 21 | ...state, 22 | loading: false 23 | } 24 | default: 25 | return state 26 | } 27 | }} 28 | > 29 | {(list, { state }) => { 30 | const { loading } = state 31 | const filterInstance = form || list.getFilterInstance() 32 | const search = list?.search || noop 33 | if (typeof render === 'function') { 34 | return render(search) 35 | } 36 | 37 | return 62 | }} 63 | 64 | } 65 | 66 | createVirtualBox('search', InternalSearch) 67 | const Search = createControllerBox('alist-search', (props) => { 68 | const { form, schema } = props 69 | const componentProps = schema.getExtendsComponentProps() 70 | return 71 | }) 72 | 73 | export { 74 | Search, 75 | InternalSearch, 76 | } -------------------------------------------------------------------------------- /packages/antd-components/src/components/Selection.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Selection } from '@alist/react' 3 | import { createVirtualBox } from '@formily/antd' 4 | import { setSelectionsByInstance } from '../shared' 5 | 6 | const InternalSelection = (props) => { 7 | const { children, render, ...others } = props 8 | const compatChildren = render || children 9 | return 10 | {(opts, list) => { 11 | let element 12 | if (typeof compatChildren === 'function') { 13 | list.setSelections = (ids, records) => { 14 | setSelectionsByInstance(list, ids, records) 15 | } 16 | element = compatChildren(opts, list) 17 | } else { 18 | element = compatChildren || React.Fragment 19 | } 20 | return element 21 | }} 22 | 23 | } 24 | 25 | const ListSelection = createVirtualBox('alist-selection', InternalSelection) 26 | 27 | export { 28 | ListSelection, 29 | InternalSelection, 30 | } -------------------------------------------------------------------------------- /packages/antd-components/src/components/ToggleTrigger.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react' 2 | import { Toggle } from '@alist/react' 3 | import { Button } from 'antd' 4 | import { createVirtualBox } from '@formily/antd' 5 | import styled, { css } from 'styled-components' 6 | 7 | const unExpandIconDefault = 8 | const expandIconDefault = 9 | 10 | const ToggleActionText = styled(forwardRef((props, ref) => { 11 | return
12 |
{props.children}
13 |
14 | }))` 15 | display: inline-block; 16 | 17 | .alist-toggle-action-text { 18 | display: flex; 19 | align-items: center; 20 | } 21 | 22 | ${props => props.color && css`color: ${props.color}` } 23 | ${props => props.fontSize && css`font-size: ${props.fontSize}` } 24 | ` 25 | 26 | const StyledBtn = styled(forwardRef((props, ref) => { 27 | return 20 | } else { 21 | return 26 | } 27 | }} 28 |
29 | } 30 | 31 | const Clear = createControllerBox('alist-clear', (props) => { 32 | const { form, schema } = props 33 | const componentProps = schema.getExtendsComponentProps() 34 | return 35 | }) 36 | 37 | export { 38 | Clear, 39 | InternalClear, 40 | } -------------------------------------------------------------------------------- /packages/next-components/src/components/Consumer.tsx: -------------------------------------------------------------------------------- 1 | import { Consumer as InternalConsumer } from '@alist/react' 2 | import { createVirtualBox } from '@formily/next' 3 | 4 | const ListSpy = createVirtualBox('alist-consumer', InternalConsumer) 5 | 6 | export { 7 | ListSpy, 8 | ListSpy as ListConsumer, 9 | InternalConsumer as InternalListSpy, 10 | InternalConsumer, 11 | } -------------------------------------------------------------------------------- /packages/next-components/src/components/ExpandContainer.tsx: -------------------------------------------------------------------------------- 1 | import { createExpandContainer } from '@alist/react' 2 | import { createControllerBox, InternalVirtualField } from '@formily/next' 3 | 4 | const ExpandContainer = createExpandContainer({ VirtualField: InternalVirtualField, createControllerBox }) 5 | 6 | export { 7 | ExpandContainer, 8 | ExpandContainer as ListExpandContainer, 9 | }; -------------------------------------------------------------------------------- /packages/next-components/src/components/ExpandTrigger.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ExpandTrigger } from '@alist/react' 3 | import { createVirtualBox, createControllerBox, FormSpy } from '@formily/next' 4 | import { Button } from '@alifd/next' 5 | 6 | const InternalExpandTrigger = (props) => { 7 | const { render, expandText, unExpandText, ...others } = props 8 | 9 | return 10 | {({ form }) => { 11 | return 12 | {({ toggleExpandStatus, expandStatus }) => { 13 | if (typeof render === 'function') { 14 | return render({ toggleExpandStatus, expandStatus }) 15 | } 16 | return 19 | }} 20 | 21 | }} 22 | 23 | } 24 | 25 | createVirtualBox('filter-expand-trigger', InternalExpandTrigger) 26 | const ListExpandTrigger = createControllerBox('alist-expand-trigger', (props) => { 27 | const { schema } = props 28 | const componentProps = schema.getExtendsComponentProps() 29 | return 30 | }) 31 | 32 | export { 33 | ListExpandTrigger, 34 | InternalExpandTrigger, 35 | } -------------------------------------------------------------------------------- /packages/next-components/src/components/List.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useRef } from 'react'; 2 | import { ListProvider, ListContext } from '@alist/react' 3 | import { createControllerBox, useFormEffects } from '@formily/next' 4 | import useNextList from '../hooks/useNextList'; 5 | 6 | const InternalList = (props) => { 7 | const opts = useNextList(props) 8 | return 9 | } 10 | 11 | const ListFilterSpy = (props) => { 12 | const settingRef = useRef(null) 13 | 14 | const { form } = props 15 | const list = useContext(ListContext) 16 | const effects = list.getFilterEffects() 17 | if (!settingRef.current) { 18 | list.setFilterInstance(form) 19 | list.initSyncFilterData(true) 20 | settingRef.current = true 21 | } 22 | 23 | useFormEffects(effects) 24 | 25 | return null 26 | } 27 | 28 | const VirtualList = (props) => { 29 | const { form, children, schema } = props 30 | const componentProps = schema.getExtendsComponentProps() 31 | 32 | return 33 | 34 | {children} 35 | 36 | } 37 | const List = createControllerBox('alist', VirtualList) 38 | 39 | export { 40 | List, 41 | VirtualList, 42 | InternalList, 43 | } -------------------------------------------------------------------------------- /packages/next-components/src/components/MultipleProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { MultipleProvider as InternalMultipleProvider } from '@alist/react' 3 | import { createControllerBox } from '@formily/next' 4 | 5 | const MultipleProvider = createControllerBox('alist-multipleProvider', (props) => { 6 | const { form, schema, children } = props 7 | const componentProps = schema.getExtendsComponentProps() 8 | return 9 | {children} 10 | 11 | }) 12 | 13 | export { 14 | MultipleProvider, 15 | InternalMultipleProvider, 16 | } -------------------------------------------------------------------------------- /packages/next-components/src/components/Pagination.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react' 2 | import { PaginationProvider } from '@alist/react' 3 | import { Pagination as NextPagination } from '@alifd/next' 4 | import { PaginationProps } from '@alifd/next/types/pagination' 5 | import { createVirtualBox } from '@formily/next' 6 | import styled from 'styled-components' 7 | 8 | const InternalPagination = styled(forwardRef((props, ref) => { 9 | return 10 | {(connectProps) => { 11 | const { currentPage, setCurrentPage, setPageSize, totalPages, pageSize, ...other } = connectProps; 12 | return 13 | }} 14 | 15 | }))` 16 | margin: 16px 0; 17 | text-align: ${props => (props.align || 'right')}; 18 | ` 19 | 20 | const Pagination = createVirtualBox('alist-pagination', InternalPagination) 21 | 22 | export { 23 | Pagination, 24 | InternalPagination, 25 | } -------------------------------------------------------------------------------- /packages/next-components/src/components/Reset.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Consumer, ListLifeCycleTypes } from '@alist/react' 3 | import { createVirtualBox, createControllerBox } from '@formily/next' 4 | import { Button } from '@alifd/next' 5 | 6 | const InternalReset = (props) => { 7 | const { form, render, content, children, ...others } = props 8 | return 9 | {(list) => { 10 | if (list) { 11 | const { reset } = list 12 | if (typeof render === 'function') { 13 | return render(reset) 14 | } 15 | return 20 | } else { 21 | return 26 | } 27 | }} 28 | 29 | } 30 | 31 | createVirtualBox('reset', InternalReset) 32 | const Reset = createControllerBox('alist-reset', (props) => { 33 | const { form, schema } = props 34 | const componentProps = schema.getExtendsComponentProps() 35 | return 36 | }) 37 | 38 | export { 39 | InternalReset, 40 | Reset 41 | } -------------------------------------------------------------------------------- /packages/next-components/src/components/Search.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Consumer, ListLifeCycleTypes } from '@alist/react' 3 | import { Button } from '@alifd/next' 4 | import { createVirtualBox, createControllerBox } from '@formily/next' 5 | 6 | const noop = () => {} 7 | const InternalSearch = (props) => { 8 | const { form, enableLoading, render, content, children, ...others } = props 9 | return { 12 | switch (action.type) { 13 | case ListLifeCycleTypes.ON_LIST_BEFORE_QUERY: 14 | return { 15 | ...state, 16 | loading: true 17 | } 18 | case ListLifeCycleTypes.ON_LIST_ERROR: 19 | case ListLifeCycleTypes.DID_LIST_UPDATE: 20 | return { 21 | ...state, 22 | loading: false 23 | } 24 | default: 25 | return state 26 | } 27 | }} 28 | > 29 | {(list, { state }) => { 30 | const filterInstance = form || list.getFilterInstance() 31 | const { loading } = state 32 | const search = list?.search || noop 33 | if (typeof render === 'function') { 34 | return render(search) 35 | } 36 | 37 | return 62 | }} 63 | 64 | } 65 | 66 | createVirtualBox('search', InternalSearch) 67 | const Search = createControllerBox('alist-search', (props) => { 68 | const { form, schema } = props 69 | const componentProps = schema.getExtendsComponentProps() 70 | return 71 | }) 72 | 73 | export { 74 | Search, 75 | InternalSearch, 76 | } -------------------------------------------------------------------------------- /packages/next-components/src/components/Selection.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Selection } from '@alist/react' 3 | import { createVirtualBox } from '@formily/next' 4 | import { setSelectionsByInstance } from '../shared' 5 | 6 | const InternalSelection = (props) => { 7 | const { children, render, ...others } = props 8 | const compatChildren = render || children 9 | return 10 | {(opts, list) => { 11 | let element 12 | if (typeof compatChildren === 'function') { 13 | list.setSelections = (ids, records) => { 14 | setSelectionsByInstance(list, ids, records) 15 | } 16 | element = compatChildren(opts, list) 17 | } else { 18 | element = compatChildren || React.Fragment 19 | } 20 | return element 21 | }} 22 | 23 | } 24 | 25 | const ListSelection = createVirtualBox('alist-selection', InternalSelection) 26 | 27 | export { 28 | ListSelection, 29 | InternalSelection, 30 | } -------------------------------------------------------------------------------- /packages/next-components/src/components/Sorter.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react' 2 | import { SorterProvider } from '@alist/react' 3 | import { createVirtualBox } from '@formily/next' 4 | import styled from 'styled-components'; 5 | 6 | const defaultAscIcon = 7 | const defaultDescIcon = 8 | 9 | const noop = () => {} 10 | const InternalSorter = styled(forwardRef((props, ref) => { 11 | const { className, render, 12 | style, 13 | ascIcon = defaultAscIcon, 14 | descIcon = defaultDescIcon, 15 | onSort = noop, 16 | ...others } = props 17 | return 18 | {({ order, setOrder }) => { 19 | if (typeof render === 'function') { 20 | return render({ order, setOrder }) 21 | } 22 | 23 | return 24 | 25 | { 27 | const nextOrder = setOrder('asc') 28 | onSort(props.dataIndex, nextOrder) 29 | }} 30 | >{ascIcon} 31 | 32 | 33 | { 35 | const nextOrder = setOrder('desc') 36 | onSort(props.dataIndex, nextOrder) 37 | }} 38 | >{descIcon} 39 | 40 | 41 | }} 42 | 43 | }))` 44 | display: inline-block; 45 | line-height: 1; 46 | margin-left: 6px; 47 | position: relative; 48 | vertical-align: middle; 49 | height: 1rem; 50 | cursor: pointer; 51 | margin-top: -4px; 52 | 53 | &.next-table-sort { 54 | width: auto; 55 | } 56 | 57 | .alist-sorter { 58 | transform: scale(0.91666667) rotate(0deg); 59 | } 60 | 61 | .alist-sorter.asc { 62 | top: 0px; 63 | } 64 | 65 | .alist-sorter.desc { 66 | top: 7.5px; 67 | } 68 | ` 69 | 70 | const Sorter = createVirtualBox('alist-sorter', InternalSorter) 71 | 72 | export { 73 | InternalSorter, 74 | Sorter, 75 | } -------------------------------------------------------------------------------- /packages/next-components/src/components/ToggleTrigger.tsx: -------------------------------------------------------------------------------- 1 | import React, { forwardRef } from 'react' 2 | import { Toggle } from '@alist/react' 3 | import { Button } from '@alifd/next' 4 | import { createVirtualBox } from '@formily/next' 5 | import styled, { css } from 'styled-components' 6 | 7 | const unExpandIconDefault = 8 | const expandIconDefault = 9 | 10 | const ToggleActionText = styled(forwardRef((props, ref) => { 11 | return
12 |
{props.children}
13 |
14 | }))` 15 | display: inline-block; 16 | 17 | .alist-toggle-action-text { 18 | display: flex; 19 | align-items: center; 20 | } 21 | 22 | ${props => props.color && css`color: ${props.color}` } 23 | ${props => props.fontSize && css`font-size: ${props.fontSize}` } 24 | ` 25 | 26 | const InternalToggleTrigger = (props) => { 27 | const { id, render, 28 | expandText, unExpandText, 29 | unExpandIcon = unExpandIconDefault, expandIcon = expandIconDefault, 30 | children, hideIcon, color, fontSize, ...others 31 | } = props 32 | return 33 | {({ toggle, toggleAll, expandStatus, expandedAllStatus }) => { 34 | if (typeof render === 'function') { 35 | return render({ toggle, toggleAll, expandStatus, expandedAllStatus }) 36 | } 37 | 38 | return 48 | }} 49 | 50 | } 51 | 52 | const SchemaListToggleTrigger = createVirtualBox('alist-toggle-trigger', InternalToggleTrigger) 53 | 54 | export { 55 | InternalToggleTrigger, 56 | InternalToggleTrigger as ListToggleTrigger, 57 | SchemaListToggleTrigger, 58 | } -------------------------------------------------------------------------------- /packages/next-components/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useNextList' 2 | export * from './useAList' -------------------------------------------------------------------------------- /packages/next-components/src/hooks/useAList.ts: -------------------------------------------------------------------------------- 1 | import useNextList from './useNextList'; 2 | // import { useTable, usePagination } from '@alist/react'; 3 | 4 | export const useAList = (props) => { 5 | const { tableProps = {}, ...others } = props; 6 | const { actions } = useNextList(others); 7 | // const table = useTable(tableProps, actions); 8 | // const pagination = usePagination({}, actions); 9 | 10 | return { 11 | actions, 12 | // table, 13 | // pagination, 14 | } 15 | } 16 | 17 | export default useAList; 18 | -------------------------------------------------------------------------------- /packages/next-components/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from '@alist/react' 2 | import { createNextListActions, createNextAsyncListActions } from './shared' 3 | export * from './components/List' 4 | export * from './components/Selection' 5 | export * from './components/Sorter' 6 | export * from './components/ExpandContainer' 7 | export * from './components/ExpandTrigger' 8 | export { 9 | MultipleProvider, 10 | InternalMultipleProvider 11 | } from './components/MultipleProvider' 12 | export * from './components/ToggleTrigger' 13 | export * from './components/Table' 14 | export * from './components/Pagination' 15 | export * from './components/Clear' 16 | export * from './components/Consumer' 17 | export * from './components/Search' 18 | export * from './components/Button' 19 | export * from './components/Reset' 20 | export * from './hooks' 21 | 22 | export { 23 | createNextListActions as createListActions, 24 | createNextAsyncListActions as createAsyncListActions 25 | } 26 | -------------------------------------------------------------------------------- /packages/next-components/src/shared.ts: -------------------------------------------------------------------------------- 1 | import { 2 | mergeActions, 3 | createActions, 4 | createAsyncActions, 5 | createListActions, 6 | createAsyncListActions 7 | } from '@alist/react' 8 | 9 | export const createNextListActions = () => { 10 | return mergeActions( 11 | createListActions(), 12 | createActions( 13 | 'setSelections', 14 | 'setRowSelection', 15 | 'getRowSelection', 16 | 'disableRowSelection' 17 | ) 18 | ) 19 | } 20 | 21 | export const createNextAsyncListActions = () => 22 | mergeActions( 23 | createAsyncListActions(), 24 | createAsyncActions( 25 | 'setSelections', 26 | 'setRowSelection', 27 | 'getRowSelection', 28 | 'disableRowSelection' 29 | ) 30 | ) 31 | 32 | export const setSelectionsByInstance = (instance, ids, records) => { 33 | const targetInstance = instance.actions ? instance.actions : instance 34 | targetInstance.setRowSelection({ 35 | ids, 36 | records 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /packages/next-components/src/style.ts: -------------------------------------------------------------------------------- 1 | import '@alifd/next/lib/table/style' 2 | import '@alifd/next/lib/pagination/style' 3 | import '@alifd/next/lib/button/style' 4 | -------------------------------------------------------------------------------- /packages/next-components/src/types.ts: -------------------------------------------------------------------------------- 1 | // import React from 'react' 2 | -------------------------------------------------------------------------------- /packages/next-components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./lib" 5 | }, 6 | "include": ["./src/**/*.js", "./src/**/*.ts", "./src/**/*.tsx"], 7 | "exclude": ["./src/__tests__/*"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/next/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | build 4 | __tests__ -------------------------------------------------------------------------------- /packages/next/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present, Alibaba Group Holding Limited. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /packages/next/build.ts: -------------------------------------------------------------------------------- 1 | import { compile, getCompileConfig } from '../../scripts/build' 2 | import ts from 'typescript' 3 | import tsImportPluginFactory from 'ts-import-plugin' 4 | import glob from 'glob' 5 | import * as fs from 'fs-extra' 6 | 7 | const transformer = tsImportPluginFactory({ 8 | libraryName: '@alifd/next', 9 | //style: importPath => `${importPath}/style`, 10 | }) 11 | 12 | function buildESM() { 13 | const { fileNames, options } = getCompileConfig(require.resolve('./tsconfig.json'), { 14 | outDir: './esm', 15 | module: ts.ModuleKind.ESNext 16 | }) 17 | compile(fileNames, options) 18 | console.log('esm build successfully') 19 | } 20 | 21 | const TEMP_OUT_DIR = './temp_esm' 22 | 23 | function buildTempESM() { 24 | const { fileNames, options } = getCompileConfig(require.resolve('./tsconfig.json'), { 25 | outDir: TEMP_OUT_DIR, 26 | module: ts.ModuleKind.ESNext 27 | }) 28 | compile(fileNames, options, { before: [transformer] }) 29 | 30 | console.log('temporary esm build successfully') 31 | } 32 | 33 | function clearTempESM() { 34 | fs.removeSync(TEMP_OUT_DIR) 35 | 36 | console.log('clear temporary esm build successfully') 37 | } 38 | 39 | function buildES5() { 40 | const rootNames = glob.sync(`${TEMP_OUT_DIR}/**/*.js`) 41 | compile(rootNames, { 42 | allowJs: true, 43 | esModuleInterop: true, 44 | moduleResolution: ts.ModuleResolutionKind.NodeJs, 45 | module: ts.ModuleKind.CommonJS, 46 | target: ts.ScriptTarget.ES5, 47 | outDir: './lib', 48 | declaration: false, 49 | }) 50 | console.log('es5 build successfully') 51 | } 52 | 53 | 54 | 55 | buildESM() 56 | 57 | buildTempESM() 58 | buildES5() 59 | clearTempESM() 60 | -------------------------------------------------------------------------------- /packages/next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@alist/next", 3 | "version": "1.0.59", 4 | "main": "lib", 5 | "engines": { 6 | "npm": ">=3.0.0" 7 | }, 8 | "scripts": { 9 | "build": "ts-node --project ../../tsconfig.build.json build.ts" 10 | }, 11 | "stylePath": "style.js", 12 | "devDependencies": { 13 | "@alifd/next": "1.x", 14 | "@types/classnames": "^2.2.9", 15 | "@types/styled-components": "4.1.8", 16 | "typescript": "^3.5.2" 17 | }, 18 | "types": "esm/index.d.ts", 19 | "peerDependencies": { 20 | "@alifd/next": "1.x", 21 | "@babel/runtime": "^7.4.4", 22 | "@formily/next": "^1.2.1", 23 | "@formily/next-components": "^1.2.1", 24 | "react": ">=16.8.0", 25 | "react-dom": ">=16.8.0", 26 | "react-eva": "^1.1.7" 27 | }, 28 | "dependencies": { 29 | "@alist/next-components": "^1.0.59", 30 | "@alist/react-schema-renderer": "^1.0.59", 31 | "@formily/next-components": "^1.2.1" 32 | }, 33 | "repository": { 34 | "type": "git", 35 | "url": "git@gitlab.alibaba-inc.com:a-lib/alist.git" 36 | }, 37 | "publishConfig": { 38 | "access": "public" 39 | }, 40 | "license": "MIT", 41 | "gitHead": "76f2ae551cdfe1a87e86f21b8647cfa46edc507a" 42 | } 43 | -------------------------------------------------------------------------------- /packages/next/src/__tests__/index.spec.tsx: -------------------------------------------------------------------------------- 1 | describe('index',()=>{ 2 | test('todo', () => { 3 | 4 | }) 5 | }) -------------------------------------------------------------------------------- /packages/next/src/components/Layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | FormButtonGroup, 4 | createVirtualBox, 5 | } from '@formily/next' 6 | import { FormItemGrid, MegaLayout } from '@formily/next-components' 7 | import { compatLayoutProps } from '@alist/react' 8 | import { ILayoutProps } from '..' 9 | 10 | let Layout: React.FC & { 11 | (): any; 12 | ButtonGroup: any; 13 | Grid: any; 14 | Flex: any; 15 | } 16 | 17 | const CompatMegaLayout = (props) => { 18 | const compatProps = compatLayoutProps(props) 19 | return 20 | } 21 | 22 | Layout = (() => { 23 | const LayoutExport: any = createVirtualBox('filter-flex-layout', CompatMegaLayout) 24 | LayoutExport.ButtonGroup = FormButtonGroup 25 | LayoutExport.Grid = FormItemGrid 26 | 27 | return LayoutExport 28 | })() 29 | 30 | 31 | export default Layout 32 | 33 | 34 | -------------------------------------------------------------------------------- /packages/next/src/components/SchemaList.tsx: -------------------------------------------------------------------------------- 1 | import SchemaRenderer from '@alist/react-schema-renderer'; 2 | 3 | export default SchemaRenderer; -------------------------------------------------------------------------------- /packages/next/src/fields/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Select, Input, Icon, CascaderSelect } from '@alifd/next' 3 | import { setup } from '@formily/next-components' 4 | import { 5 | registerFormField, 6 | mapStyledProps, 7 | mapTextComponent, 8 | connect 9 | } from '@formily/next' 10 | 11 | // 内置formily组件 12 | setup() 13 | 14 | // 搜索框的样式 15 | const presetSearchStyle = component => { 16 | return props => { 17 | return React.createElement(component, { 18 | innerBefore: , 19 | ...props 20 | }) 21 | } 22 | } 23 | 24 | // 注册cascaderSelect组件 25 | registerFormField( 26 | 'cascaderSelect', 27 | connect({ 28 | getProps: mapStyledProps, 29 | getComponent: mapTextComponent 30 | })(CascaderSelect) 31 | ) 32 | 33 | // 注册输入组件 34 | registerFormField( 35 | 'input', 36 | connect({ 37 | getProps: mapStyledProps, 38 | getComponent: mapTextComponent 39 | })(Input) 40 | ) 41 | 42 | // 注册搜索组件 43 | registerFormField( 44 | 'search', 45 | connect({ 46 | getProps: mapStyledProps, 47 | getComponent: mapTextComponent 48 | })(presetSearchStyle(Input)) 49 | ) 50 | 51 | // 注册select组件 52 | registerFormField( 53 | 'select', 54 | connect({ 55 | getProps: mapStyledProps, 56 | getComponent: mapTextComponent 57 | })(Select) 58 | ) 59 | 60 | const Preview = (props) =>

61 | {props.content} 62 |

63 | 64 | // 注册select组件 65 | registerFormField( 66 | 'preview', 67 | connect({ 68 | getProps: (props: any, fieldProps) => { 69 | const { content } = fieldProps.props || {} 70 | props.content = content 71 | } 72 | })(Preview) 73 | ) 74 | 75 | -------------------------------------------------------------------------------- /packages/next/src/index.ts: -------------------------------------------------------------------------------- 1 | import { registerListComponent } from '@alist/react-schema-renderer' 2 | export * from '@alist/react' 3 | import { 4 | InternalList as List, 5 | InternalSelection as Selection, 6 | InternalSorter as Sorter, 7 | ExpandContainer, 8 | InternalExpandTrigger as ExpandTrigger, 9 | InternalToggleTrigger as ToggleTrigger, 10 | InternalTable as Table, 11 | InternalPagination as Pagination, 12 | InternalClear as Clear, 13 | InternalConsumer as Consumer, 14 | InternalSearch as Search, 15 | InternalReset as Reset, 16 | createListActions, 17 | createAsyncListActions 18 | } from '@alist/next-components' 19 | 20 | import SchemaList from './components/SchemaList' 21 | import Filter from './components/Filter' 22 | import Layout from './components/Layout' 23 | 24 | import './fields' 25 | export * from '@alist/react-schema-renderer' 26 | export * from '@formily/next-components' 27 | export * from '@formily/next' 28 | 29 | import { FormSlot } from '@formily/next' 30 | 31 | // 传入默认组件 32 | registerListComponent({ 33 | FormSlot, 34 | List, 35 | Table, 36 | Filter, 37 | Clear, 38 | Reset, 39 | Search, 40 | Layout, 41 | Pagination, 42 | ToggleTrigger, 43 | ExpandTrigger, 44 | ExpandContainer, 45 | Selection, 46 | Consumer 47 | }) 48 | 49 | export { 50 | List, 51 | Selection, 52 | Sorter, 53 | Layout, 54 | ExpandContainer, 55 | ExpandTrigger, 56 | SchemaList, 57 | ToggleTrigger, 58 | Filter, 59 | Table, 60 | Pagination, 61 | Clear, 62 | createListActions, 63 | createAsyncListActions, 64 | Consumer, 65 | Search, 66 | Reset 67 | } 68 | -------------------------------------------------------------------------------- /packages/next/src/style.ts: -------------------------------------------------------------------------------- 1 | import '@alifd/next/lib/button/style' 2 | import '@alifd/next/lib/pagination/style' 3 | import '@alifd/next/lib/input/style' 4 | import '@alifd/next/lib/cascader-select/style' 5 | import '@alifd/next/lib/select/style' 6 | -------------------------------------------------------------------------------- /packages/next/src/types.ts: -------------------------------------------------------------------------------- 1 | // import React from 'react' 2 | -------------------------------------------------------------------------------- /packages/next/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./lib" 5 | }, 6 | "include": ["./src/**/*.js", "./src/**/*.ts", "./src/**/*.tsx"], 7 | "exclude": ["./src/__tests__/*"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/react-schema-renderer/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | build 4 | __tests__ -------------------------------------------------------------------------------- /packages/react-schema-renderer/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present, Alibaba Group Holding Limited. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /packages/react-schema-renderer/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/alist/d03650f3a7cdfb4c4ad71142d10d79d8ddfd3bf4/packages/react-schema-renderer/README.md -------------------------------------------------------------------------------- /packages/react-schema-renderer/mock.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 0, 3 | "data": { 4 | "currentPage": 1, 5 | "dataLsit": [ 6 | { 7 | "label": "data #0", 8 | "value": 0 9 | }, 10 | { 11 | "label": "data #1", 12 | "value": 1 13 | }, 14 | { 15 | "label": "data #2", 16 | "value": 2 17 | }, 18 | { 19 | "label": "data #3", 20 | "value": 3 21 | }, 22 | { 23 | "label": "data #4", 24 | "value": 4 25 | }, 26 | { 27 | "label": "data #5", 28 | "value": 5 29 | }, 30 | { 31 | "label": "data #6", 32 | "value": 6 33 | }, 34 | { 35 | "label": "data #7", 36 | "value": 7 37 | }, 38 | { 39 | "label": "data #8", 40 | "value": 8 41 | }, 42 | { 43 | "label": "data #9", 44 | "value": 9 45 | }, 46 | { 47 | "label": "data #10", 48 | "value": 10 49 | }, 50 | { 51 | "label": "data #11", 52 | "value": 11 53 | }, 54 | { 55 | "label": "data #12", 56 | "value": 12 57 | }, 58 | { 59 | "label": "data #13", 60 | "value": 13 61 | }, 62 | { 63 | "label": "data #14", 64 | "value": 14 65 | }, 66 | { 67 | "label": "data #15", 68 | "value": 15 69 | }, 70 | { 71 | "label": "data #16", 72 | "value": 16 73 | }, 74 | { 75 | "label": "data #17", 76 | "value": 17 77 | }, 78 | { 79 | "label": "data #18", 80 | "value": 18 81 | }, 82 | { 83 | "label": "data #19", 84 | "value": 19 85 | } 86 | ], 87 | "pageSize": 10, 88 | "total": 20, 89 | "totalPages": 2 90 | } 91 | } -------------------------------------------------------------------------------- /packages/react-schema-renderer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@alist/react-schema-renderer", 3 | "version": "1.0.59", 4 | "main": "lib", 5 | "types": "lib/index.d.ts", 6 | "engines": { 7 | "npm": ">=3.0.0" 8 | }, 9 | "scripts": { 10 | "build": "rm -rf lib && tsc --declaration" 11 | }, 12 | "devDependencies": { 13 | "@alifd/next": "1.x", 14 | "typescript": "^3.5.2" 15 | }, 16 | "dependencies": { 17 | "@alist/react": "^1.0.59" 18 | }, 19 | "peerDependencies": { 20 | "@alist/react": "^1.0.0", 21 | "@babel/runtime": "^7.4.4", 22 | "@types/react": "^16.8.23", 23 | "react": ">=16.8.0", 24 | "react-dom": ">=16.8.0" 25 | }, 26 | "repository": { 27 | "type": "git", 28 | "url": "git@gitlab.alibaba-inc.com:a-lib/alist.git" 29 | }, 30 | "publishConfig": { 31 | "access": "public" 32 | }, 33 | "license": "MIT", 34 | "gitHead": "76f2ae551cdfe1a87e86f21b8647cfa46edc507a" 35 | } 36 | -------------------------------------------------------------------------------- /packages/react-schema-renderer/src/__tests__/index.spec.tsx: -------------------------------------------------------------------------------- 1 | describe('index',()=>{ 2 | test('todo', () => { 3 | 4 | }) 5 | }) -------------------------------------------------------------------------------- /packages/react-schema-renderer/src/shared.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | let baseRegistry = { 4 | div: props =>
5 | } 6 | 7 | let globalComponentsRegistry = {} 8 | let globalFuncs = {} 9 | 10 | export const getComponentsRegistry = () => ({ 11 | ...baseRegistry, 12 | ...globalComponentsRegistry 13 | }) 14 | 15 | export const getFuncsRegistry = () => ({ 16 | ...globalFuncs 17 | }) 18 | 19 | export const registerListComponent = (...args) => { 20 | if (args.length === 1) { 21 | Object.keys(args[0]).forEach(k => { 22 | if (args[0][k]) { 23 | globalComponentsRegistry[k] = args[0][k] 24 | } 25 | }) 26 | } else { 27 | globalComponentsRegistry[args[0]] = args[1] 28 | } 29 | } 30 | 31 | export const registerListFuncs = (...args) => { 32 | if (args.length === 1) { 33 | Object.keys(args[0]).forEach(k => { 34 | if (args[0][k]) { 35 | globalFuncs[k] = args[0][k] 36 | } 37 | }) 38 | } else { 39 | globalFuncs[args[0]] = args[1] 40 | } 41 | } 42 | 43 | export const cleanListComponentRegistry = () => { 44 | globalComponentsRegistry = {} 45 | } 46 | 47 | export const cleanListFunctionRegistry = () => { 48 | globalFuncs = {} 49 | } 50 | -------------------------------------------------------------------------------- /packages/react-schema-renderer/src/types.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/alist/d03650f3a7cdfb4c4ad71142d10d79d8ddfd3bf4/packages/react-schema-renderer/src/types.ts -------------------------------------------------------------------------------- /packages/react-schema-renderer/src/util/index.tsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alibaba/alist/d03650f3a7cdfb4c4ad71142d10d79d8ddfd3bf4/packages/react-schema-renderer/src/util/index.tsx -------------------------------------------------------------------------------- /packages/react-schema-renderer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./lib" 5 | }, 6 | "include": [ 7 | "./src/**/*.ts", 8 | "./src/**/*.tsx" 9 | ], 10 | "exclude": [ 11 | "./src/__tests__/*" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/react/.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | build 4 | __tests__ -------------------------------------------------------------------------------- /packages/react/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present, Alibaba Group Holding Limited. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /packages/react/mock.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": 0, 3 | "data": { 4 | "currentPage": 1, 5 | "dataLsit": [ 6 | { 7 | "label": "data #0", 8 | "value": 0 9 | }, 10 | { 11 | "label": "data #1", 12 | "value": 1 13 | }, 14 | { 15 | "label": "data #2", 16 | "value": 2 17 | }, 18 | { 19 | "label": "data #3", 20 | "value": 3 21 | }, 22 | { 23 | "label": "data #4", 24 | "value": 4 25 | }, 26 | { 27 | "label": "data #5", 28 | "value": 5 29 | }, 30 | { 31 | "label": "data #6", 32 | "value": 6 33 | }, 34 | { 35 | "label": "data #7", 36 | "value": 7 37 | }, 38 | { 39 | "label": "data #8", 40 | "value": 8 41 | }, 42 | { 43 | "label": "data #9", 44 | "value": 9 45 | }, 46 | { 47 | "label": "data #10", 48 | "value": 10 49 | }, 50 | { 51 | "label": "data #11", 52 | "value": 11 53 | }, 54 | { 55 | "label": "data #12", 56 | "value": 12 57 | }, 58 | { 59 | "label": "data #13", 60 | "value": 13 61 | }, 62 | { 63 | "label": "data #14", 64 | "value": 14 65 | }, 66 | { 67 | "label": "data #15", 68 | "value": 15 69 | }, 70 | { 71 | "label": "data #16", 72 | "value": 16 73 | }, 74 | { 75 | "label": "data #17", 76 | "value": 17 77 | }, 78 | { 79 | "label": "data #18", 80 | "value": 18 81 | }, 82 | { 83 | "label": "data #19", 84 | "value": 19 85 | } 86 | ], 87 | "pageSize": 10, 88 | "total": 20, 89 | "totalPages": 2 90 | } 91 | } -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@alist/react", 3 | "version": "1.0.59", 4 | "main": "lib", 5 | "types": "lib/index.d.ts", 6 | "engines": { 7 | "npm": ">=3.0.0" 8 | }, 9 | "scripts": { 10 | "build": "rm -rf lib && tsc --declaration" 11 | }, 12 | "devDependencies": { 13 | "typescript": "^3.5.2" 14 | }, 15 | "dependencies": { 16 | "@alist/core": "^1.0.59" 17 | }, 18 | "peerDependencies": { 19 | "@babel/runtime": "^7.4.4", 20 | "@types/react": "^16.8.23", 21 | "react": ">=16.8.0", 22 | "react-dom": ">=16.8.0", 23 | "react-eva": "^1.1.7" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git@gitlab.alibaba-inc.com:a-lib/alist.git" 28 | }, 29 | "publishConfig": { 30 | "access": "public" 31 | }, 32 | "license": "MIT", 33 | "gitHead": "76f2ae551cdfe1a87e86f21b8647cfa46edc507a" 34 | } 35 | -------------------------------------------------------------------------------- /packages/react/src/__tests__/index.spec.tsx: -------------------------------------------------------------------------------- 1 | describe('index',()=>{ 2 | test('todo', () => { 3 | 4 | }) 5 | }) -------------------------------------------------------------------------------- /packages/react/src/components/ConnectProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import FieldProvider from './FieldProvider' 3 | 4 | const ConnectProvider: React.FC = (props = {}) => { 5 | return 6 | } 7 | 8 | export default ConnectProvider -------------------------------------------------------------------------------- /packages/react/src/components/Consumer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import useConsumer from '../hooks/useConsumer' 3 | 4 | const ConsumerProvider: React.FC = (props = {}) => { 5 | const { children, render, ...others } = props 6 | const { list, state, type, listDomain } = useConsumer(others) 7 | const comaptChildren = render || children 8 | 9 | let element 10 | if (typeof comaptChildren === 'function') { 11 | const compatList = list 12 | element = comaptChildren(compatList,{ state, type, listDomain }) 13 | } else { 14 | element = comaptChildren || React.Fragment 15 | } 16 | 17 | return element 18 | } 19 | 20 | export default ConsumerProvider -------------------------------------------------------------------------------- /packages/react/src/components/ExpandContainer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import useExpandContainer from '../hooks/useExpandContainer' 3 | 4 | const createExpandContainer = ({ createControllerBox, VirtualField }) => { 5 | return createControllerBox('filter-expand-container', (options) => { 6 | const targetPath = `${options.schema.path}_real_vf` 7 | useExpandContainer({ ...options, targetPath }) 8 | return {options.children} 9 | }) 10 | } 11 | 12 | export default createExpandContainer -------------------------------------------------------------------------------- /packages/react/src/components/ExpandTrigger.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import useExpand from '../hooks/useExpand' 3 | 4 | const ExpandTrigger: React.FC = (props = {}) => { 5 | const { children } = props 6 | const { expandStatus, list, toggleExpandStatus } = useExpand(props) 7 | 8 | let element 9 | if (typeof children === 'function') { 10 | element = children({ expandStatus, toggleExpandStatus }, list) 11 | } else { 12 | element = children || React.Fragment 13 | } 14 | 15 | return element 16 | } 17 | 18 | export default ExpandTrigger -------------------------------------------------------------------------------- /packages/react/src/components/FieldProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect } from 'react' 2 | import ListContext from '../context' 3 | import { ListLifeCycleTypes } from '@alist/core' 4 | import FilterModeContext from '../context/filterMode' 5 | import useForceUpdate from '../hooks/useForceUpdate' 6 | 7 | const getFormatName = (name) => { 8 | return name 9 | } 10 | 11 | const getValidateConfigByName = (validateConfig: any, name: string) => { 12 | // TODO: 对象表单 13 | // TODO: 数组类表单 14 | return validateConfig[name] 15 | } 16 | 17 | const FieldProvider: React.FC = (props = {}) => { 18 | const { children, name, connectMode = false, defaultEmptyValue = null, searchOnChange = false } = props 19 | const list = useContext(ListContext) 20 | const { mode } = useContext(FilterModeContext) || {} 21 | const formatName = getFormatName(name) 22 | const formValidateConfig = list.getValidateConfig() 23 | const validateConfig = getValidateConfigByName(formValidateConfig, formatName) 24 | 25 | const forceUpdate = useForceUpdate() 26 | const refresh = (names) => { 27 | if (names === '*' || names.some(n => n === name)) { 28 | forceUpdate() 29 | } 30 | } 31 | 32 | const clean = () => { 33 | if (connectMode && name) { 34 | list.setFilterData({ [name]: defaultEmptyValue }) 35 | refresh([name]) 36 | } 37 | } 38 | 39 | const setValue = (value) => { 40 | if (connectMode && name) { 41 | list.setFilterData({ [name]: value }) 42 | refresh([name]) 43 | if (searchOnChange) { 44 | list.refresh() 45 | } 46 | } 47 | } 48 | 49 | const filterData = list.getFilterData() 50 | const value = name ? (filterData[name] === undefined ? defaultEmptyValue :filterData[name]) : defaultEmptyValue 51 | 52 | useEffect(() => { 53 | const id = list.subscribe(ListLifeCycleTypes.ON_LIST_FILTER_REFRESH, refresh) 54 | return function cleanup() { 55 | list.unSubscribe(id) 56 | } 57 | }, []) 58 | 59 | useEffect(() => { 60 | const id = list.subscribe(ListLifeCycleTypes.ON_LIST_CLEAR, clean) 61 | return function cleanup() { 62 | list.unSubscribe(id) 63 | } 64 | }, []) 65 | 66 | let element 67 | if (typeof children === 'function') { 68 | element = children({ 69 | validateConfig, 70 | mode, 71 | setValue, 72 | value, 73 | }, list) 74 | } else { 75 | element = children || React.Fragment 76 | } 77 | return element 78 | } 79 | 80 | export default FieldProvider -------------------------------------------------------------------------------- /packages/react/src/components/FilterProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import useFilter from '../hooks/useFilter' 3 | import FilterModeContext from '../context/filterMode' 4 | 5 | const FilterProvider: React.FC = (props = {}) => { 6 | const { mode, children } = props 7 | const { filterInstance, list } = useFilter(props) 8 | 9 | let element 10 | if (typeof children === 'function') { 11 | element = children({ 12 | filterInstance, 13 | }, list) 14 | } else { 15 | element = children || React.Fragment 16 | } 17 | 18 | return 19 | {element} 20 | 21 | } 22 | 23 | export default FilterProvider -------------------------------------------------------------------------------- /packages/react/src/components/Layout.tsx: -------------------------------------------------------------------------------- 1 | export const compatLayoutProps = (props) => { 2 | const { mode, autoRow = true, labelAlign = 'top', ...others } = props 3 | const grid = (!mode || mode === 'columns') && !props.inline 4 | const layoutProps: any = { 5 | grid, 6 | autoRow, 7 | ...others 8 | } 9 | if (!props.inset) layoutProps.labelAlign = labelAlign 10 | if (props.inset) layoutProps.full = true 11 | return layoutProps 12 | } 13 | 14 | export const compatLayoutItemProps = (props) => { 15 | const compatProps = {...(props || {})} 16 | const megaKeys = ['full', 'labelAlign', 'span', 'hasBorder', 'labelCol', 'wrapperCol', 'labelWidth', 'wrapperWidth'].filter(k => k in (props.props || {})) 17 | if (megaKeys.length) { 18 | if (!compatProps.props['x-mega-props']) compatProps.props['x-mega-props'] = {} 19 | megaKeys.forEach(k => { 20 | compatProps.props['x-mega-props'][k] = props.props[k] 21 | }) 22 | } 23 | return compatProps 24 | } -------------------------------------------------------------------------------- /packages/react/src/components/ListProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useContext } from 'react' 2 | import ListContext from '../context' 3 | import ListPropsContext from '../context/listProps' 4 | import useList from '../hooks/useList' 5 | import { ListLifeCycleTypes } from '@alist/core'; 6 | 7 | const ListProvider: React.FC = (props = {}) => { 8 | const { className, children, style, ...others } = props || {} 9 | const reuseList = useContext(ListContext) 10 | const list = reuseList || useList(others) 11 | let element 12 | if (typeof children === 'function') { 13 | element = children(list) 14 | } else { 15 | element = children || React.Fragment 16 | } 17 | 18 | useEffect(() => { 19 | list.notify(ListLifeCycleTypes.ON_LIST_MOUNTED) 20 | const filterInstance = list.getFilterInstance() 21 | if (filterInstance) { 22 | filterInstance.notify(ListLifeCycleTypes.ON_LIST_MOUNTED) 23 | } 24 | }, []) 25 | 26 | return ( 27 | 28 | 29 |
30 | {element} 31 |
32 |
33 |
34 | ) 35 | } 36 | 37 | export default ListProvider -------------------------------------------------------------------------------- /packages/react/src/components/LoadingProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import useLoading from '../hooks/useLoading' 3 | 4 | const LoadingProvider: React.FC = (props = {}) => { 5 | const { children } = props 6 | const { loading } = useLoading(props) 7 | 8 | let element 9 | if (typeof children === 'function') { 10 | element = children(loading) 11 | } else { 12 | element = children || React.Fragment 13 | } 14 | 15 | return element; 16 | } 17 | 18 | export default LoadingProvider -------------------------------------------------------------------------------- /packages/react/src/components/MultipleProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import MultipleContext from '../context/multiple' 3 | import useMultipleProvider from '../hooks/useMutlipleProvider' 4 | import { EmptyStatusType } from '@alist/core' 5 | 6 | const MultipleProvider: React.FC = (props = {}) => { 7 | const { children, hideWhen } = props 8 | const { list, id, pageSize } = useMultipleProvider(props) 9 | 10 | let element 11 | if (typeof children === 'function') { 12 | element = children(list) 13 | } else { 14 | element = children || React.Fragment 15 | } 16 | 17 | if (hideWhen) { 18 | const arrHideWhen = [...hideWhen] 19 | const emptyStatus = list.getEmptyStatus() 20 | let needHide = arrHideWhen.findIndex(item => item === emptyStatus) !== -1 21 | 22 | if (hideWhen.indexOf(EmptyStatusType.EMPTY)) { 23 | const multipleData = list.getMultipleData() 24 | const { paginationDataList } = multipleData[id] || {} 25 | if ((paginationDataList || []).length === 0) { 26 | needHide = true 27 | } 28 | } 29 | 30 | if (needHide) { 31 | element = null 32 | } 33 | } 34 | 35 | return 36 | {element} 37 | 38 | } 39 | 40 | export default MultipleProvider -------------------------------------------------------------------------------- /packages/react/src/components/PaginationProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import usePagination from '../hooks/usePagination' 3 | 4 | const PaginationProvider: React.FC = (props = {}) => { 5 | const { children } = props 6 | const { hideWhenInvalid, list, pageData, setCurrentPage, setPageSize } = usePagination(props) 7 | 8 | let element 9 | if (typeof children === 'function') { 10 | element = children({ 11 | ...pageData, 12 | setCurrentPage, 13 | setPageSize, 14 | }, list) 15 | } else { 16 | element = children || React.Fragment 17 | } 18 | 19 | if (hideWhenInvalid && pageData.total === 0) { 20 | element = null 21 | } 22 | 23 | return element 24 | } 25 | 26 | export default PaginationProvider -------------------------------------------------------------------------------- /packages/react/src/components/Selection.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ListLifeCycleTypes } from '@alist/core' 3 | import useConsumer from '../hooks/useConsumer' 4 | 5 | const Selection: React.FC = (props = {}) => { 6 | const { children, selector, ...others } = props 7 | const { list } = useConsumer({ 8 | ...others, 9 | selector: selector || [ListLifeCycleTypes.ON_LIST_SELECTION_REFRESH, ListLifeCycleTypes.ON_LIST_TABLE_REFRESH] 10 | }) 11 | 12 | let element 13 | if (typeof children === 'function') { 14 | const selectionConfig = list.getSelectionConfig() 15 | let config = null 16 | if (selectionConfig) { 17 | const dataSource = list.getPaginationDataSource() 18 | const { ids, allIds, validRecords } = selectionConfig 19 | config = { 20 | ...selectionConfig, 21 | allIds, 22 | dataSource, 23 | selectedAll: (validRecords.length === (ids || []).length) && validRecords.length > 0, 24 | selectedNone: (ids || []).length === 0 25 | } 26 | } 27 | 28 | element = children(config, list) 29 | } else { 30 | element = children || React.Fragment 31 | } 32 | 33 | return element 34 | } 35 | 36 | export default Selection -------------------------------------------------------------------------------- /packages/react/src/components/SorterProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { ListLifeCycleTypes } from '@alist/core' 3 | import useConsumer from '../hooks/useConsumer' 4 | 5 | const Sorter: React.FC = (props = {}) => { 6 | const { dataIndex, children, ...others } = props 7 | const { list } = useConsumer({ 8 | ...others, 9 | selector: [ListLifeCycleTypes.ON_LIST_SORT, ListLifeCycleTypes.ON_LIST_TABLE_REFRESH] 10 | }) 11 | 12 | if (!dataIndex) { 13 | return null 14 | } 15 | 16 | let element 17 | if (typeof children === 'function') { 18 | const sortConfig = list.getSortConfig() 19 | const { sorter } = sortConfig 20 | const order = sorter[dataIndex] 21 | const setOrder = (nextOrder) => { 22 | const formatOrder = (nextOrder === order) ? undefined : nextOrder 23 | list.notify(ListLifeCycleTypes.ON_LIST_SORT, { sorter: { [dataIndex]: formatOrder }}) 24 | return formatOrder 25 | } 26 | element = children({ order, setOrder }, list) 27 | } else { 28 | element = children || React.Fragment 29 | } 30 | 31 | return element 32 | } 33 | 34 | export default Sorter -------------------------------------------------------------------------------- /packages/react/src/components/TableProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import useTable from '../hooks/useTable' 3 | import { ITableProps } from '../types' 4 | import TableContext from '../context/table' 5 | 6 | const TableProvider: React.FC = (props: ITableProps = {}) => { 7 | const { children } = props 8 | const { hideWhenInvalid = false, loading, dataSource, list, tableProps, primaryKey } = useTable(props) 9 | 10 | let element 11 | if (typeof children === 'function') { 12 | element = children({ dataSource, loading, ...tableProps }, list) 13 | } else { 14 | element = children || React.Fragment 15 | } 16 | 17 | if (hideWhenInvalid && dataSource.length === 0) { 18 | element = null 19 | } 20 | 21 | return 22 | {element} 23 | 24 | } 25 | 26 | export default TableProvider -------------------------------------------------------------------------------- /packages/react/src/components/Toggle.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react' 2 | import { ListLifeCycleTypes } from '@alist/core' 3 | import ToggleContext from '../context/toggle' 4 | import { useConsumer } from '../hooks/useConsumer' 5 | 6 | const isValidId = (id) => ([null, undefined].indexOf(id) === -1) 7 | 8 | const Toggle = (props) => { 9 | const { id, children, ...others } = props 10 | const { list, state }= useConsumer({ 11 | ...others, 12 | selector: [ 13 | ListLifeCycleTypes.ON_LIST_TOGGLE, 14 | ListLifeCycleTypes.ON_LIST_TABLE_REFRESH, 15 | 'onSetOpenRowKeys' 16 | ], 17 | reducer: (state, action: any) => { 18 | if (action.type === 'onSetOpenRowKeys') { 19 | const { payload } = action || {} 20 | const { expanded, expandedAll, defaultExpandAll } = payload || {} 21 | const nextState = { 22 | ...state, 23 | expanded, 24 | expandedAll, 25 | } 26 | 27 | if ('defaultExpandAll' in payload) { 28 | nextState.defaultExpandAll = defaultExpandAll 29 | } 30 | 31 | return nextState 32 | } else { 33 | return state 34 | } 35 | } 36 | }) 37 | const { toggle, openRowKeys } = useContext(ToggleContext) || {} 38 | let expandStatus 39 | let expandedAllStatus 40 | if (isValidId(id)) { 41 | expandStatus = openRowKeys.indexOf(id) !== -1 ? 'expand' : 'collapse' 42 | } else { 43 | if (state) { 44 | if (state.expandedAll === 'none') { 45 | expandedAllStatus = 'collapse' 46 | } else if (state.expandedAll === 'all') { 47 | expandedAllStatus = 'expand' 48 | } else { 49 | expandedAllStatus = state.defaultExpandAll ? 'expand' : 'collapse' 50 | } 51 | } 52 | } 53 | 54 | let element 55 | if (typeof children === 'function') { 56 | element = children({ 57 | toggle: () => { 58 | if (isValidId(id)) { 59 | id && toggle(id) 60 | } 61 | }, 62 | toggleAll : (...args) => { 63 | if (list && list.actions && list.actions.toggleAll) { 64 | list.actions.toggleAll(...args) 65 | } 66 | }, 67 | expandStatus, 68 | expandedAllStatus, 69 | }) 70 | } else { 71 | element = children || React.Fragment 72 | } 73 | 74 | return element 75 | } 76 | 77 | export default Toggle -------------------------------------------------------------------------------- /packages/react/src/components/ToggleProvider.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import useToggle from '../hooks/useToggle' 3 | 4 | const ToggleProvider: React.FC = (props = {}) => { 5 | const { children } = props 6 | const { toggle, toggleAll, openRowKeys } = useToggle(props) 7 | 8 | let element 9 | if (typeof children === 'function') { 10 | element = children({ toggle, toggleAll, openRowKeys }) 11 | } else { 12 | element = children || React.Fragment 13 | } 14 | 15 | return element 16 | } 17 | 18 | export default ToggleProvider -------------------------------------------------------------------------------- /packages/react/src/context/filterMode.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react' 2 | import { IFilterMode } from '../types' 3 | 4 | const Context = createContext(null) 5 | 6 | export default Context 7 | -------------------------------------------------------------------------------- /packages/react/src/context/index.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react' 2 | import { IContext } from '@alist/core/lib/types' 3 | 4 | const Context = createContext(null) 5 | 6 | export default Context 7 | -------------------------------------------------------------------------------- /packages/react/src/context/listDomain.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react' 2 | import { ListDomain } from '../shared' 3 | 4 | const Context = createContext(null) 5 | 6 | export default Context 7 | -------------------------------------------------------------------------------- /packages/react/src/context/listProps.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react' 2 | import { ITableProps } from '../types' 3 | 4 | const Context = createContext(null) 5 | 6 | export default Context 7 | -------------------------------------------------------------------------------- /packages/react/src/context/multiple.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react' 2 | import { IMultipleContext } from '@alist/core/lib/types' 3 | 4 | const Context = createContext(null) 5 | 6 | export default Context 7 | -------------------------------------------------------------------------------- /packages/react/src/context/table.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react' 2 | import { IListTableContext } from '../types' 3 | 4 | const Context = createContext(null) 5 | 6 | export default Context 7 | -------------------------------------------------------------------------------- /packages/react/src/context/toggle.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react' 2 | import { IListToggleContext } from '../types' 3 | 4 | const Context = createContext(null) 5 | 6 | export default Context 7 | -------------------------------------------------------------------------------- /packages/react/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useConsumer' 2 | export * from './useFilter' 3 | export * from './useFilterItem' 4 | export * from './useForceUpdate' 5 | export * from './useList' 6 | export * from './useMutlipleProvider' 7 | export * from './usePagination' 8 | export * from './useTable' 9 | export * from './useToggle' 10 | export * from './useListDomain' 11 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useConsumer.ts: -------------------------------------------------------------------------------- 1 | import { useState, useContext, useEffect, useReducer, useCallback } from 'react' 2 | import ListContext from '../context' 3 | import ListDomainContext from '../context/listDomain' 4 | import { ListLifeCycleTypes, IList } from '@alist/core' 5 | import useForceUpdate from './useForceUpdate' 6 | import { ListDomain } from '../shared' 7 | import { IConsumerProps } from '../types' 8 | 9 | const noop = s => s 10 | export const useConsumer = (props: IConsumerProps, propsList?: IList): { 11 | list: IList, 12 | type: string, 13 | state: any, 14 | listDomain: ListDomain 15 | } => { 16 | const { form, selector, reducer = noop } = props 17 | const formatSelector = selector || ['*'] 18 | const list = propsList || useContext(ListContext) 19 | const listDomain = useContext(ListDomainContext) || {} as ListDomain 20 | const [type, setType] = useState(ListLifeCycleTypes.ON_LIST_INIT) 21 | 22 | const [state, dispatch] = useReducer( 23 | (state, action) => reducer(state, action, list), 24 | props.initialState || {} 25 | ) 26 | 27 | const forceUpdate = useForceUpdate() 28 | const refresh = () => { 29 | forceUpdate() 30 | } 31 | 32 | // 上帝模式,默认所有事件都监听, 命中相关事件会触发重绘 33 | const eventHandler = useCallback(({ type: currentType, payload, ctx }) => { 34 | if (formatSelector.indexOf('*') !== -1 || (formatSelector.indexOf(currentType) !== -1)) { 35 | setType(currentType) 36 | dispatch({ 37 | type: currentType, 38 | payload 39 | }) 40 | refresh() 41 | } 42 | }, [dispatch]) 43 | 44 | useEffect(() => { 45 | if (list) { 46 | const id = list.subscribe(ListLifeCycleTypes.LIST_LIFECYCLES_GOD_MODE, eventHandler) 47 | return function cleanup() { 48 | list.unSubscribe(id) 49 | } 50 | } 51 | }, [list]) 52 | 53 | useEffect(() => { 54 | if (form) { 55 | const id = form.subscribe(({ type, payload }) => { 56 | if (type === ListLifeCycleTypes.LIST_LIFECYCLES_FORM_GOD_MODE) { 57 | eventHandler(payload) 58 | } 59 | }) 60 | return function cleanup() { 61 | form.unsubscribe(id) 62 | } 63 | } 64 | }, [form]) 65 | 66 | return { 67 | list, 68 | type, 69 | state, 70 | listDomain, 71 | } 72 | } 73 | 74 | export default useConsumer 75 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useExpand.ts: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect } from 'react' 2 | import ListContext from '../context' 3 | import { ListLifeCycleTypes } from '@alist/core' 4 | import useForceUpdate from './useForceUpdate' 5 | import { IConsumerProps } from '../types' 6 | 7 | export const useExpand = (props: IConsumerProps) => { 8 | const { form } = props 9 | const list = useContext(ListContext) 10 | const formInstance = form || list.getFilterInstance() 11 | const forceUpdate = useForceUpdate() 12 | const refresh = () => { 13 | forceUpdate() 14 | } 15 | 16 | useEffect(() => { 17 | const fnRef = formInstance.subscribe(({ type }) => { 18 | if ([ListLifeCycleTypes.ON_LIST_FILTER_ITEM_EXPAND, 19 | ListLifeCycleTypes.ON_LIST_FILTER_ITEM_COLLAPSE, 20 | ListLifeCycleTypes.ON_LIST_EXPAND_STATUS_SYNC, 21 | ].includes(type)) { 22 | refresh() 23 | } 24 | }) 25 | return function cleanup() { 26 | formInstance.unsubscribe(fnRef) 27 | } 28 | }, [formInstance]) 29 | 30 | const statsProps: any = {} 31 | if (list) { 32 | statsProps.expandStatus = list.getExpandStatus() 33 | statsProps.toggleExpandStatus = list.toggleExpandStatus 34 | } else if (formInstance) { 35 | statsProps.expandStatus = form.getFormState(state => state.expandStatus) 36 | statsProps.toggleExpandStatus = () => { 37 | formInstance.notify(statsProps.expandStatus === 'expand' ? 38 | ListLifeCycleTypes.ON_LIST_FILTER_ITEM_COLLAPSE : ListLifeCycleTypes.ON_LIST_FILTER_ITEM_EXPAND) 39 | } 40 | } 41 | 42 | return { 43 | ...statsProps, 44 | form: formInstance. 45 | list 46 | } 47 | } 48 | 49 | export default useExpand 50 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useExpandContainer.ts: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect } from 'react' 2 | import ListContext from '../context' 3 | import { ListLifeCycleTypes } from '@alist/core' 4 | import { IExpandProps } from '../types' 5 | 6 | export const useExpandContainer = (props: IExpandProps) => { 7 | const list = useContext(ListContext) 8 | const { targetPath, form, schema } = props 9 | const componentProps = schema.getExtendsComponentProps() 10 | const { expandStatus: propExpandStatus } = componentProps 11 | const setDisplay = (display) => { 12 | form.setFormState(state => state.expandStatus = display ? 'expand' : 'collapse') 13 | form.notify(ListLifeCycleTypes.ON_LIST_EXPAND_STATUS_SYNC) 14 | form.setFieldState(targetPath, state => { 15 | state.display = display 16 | }) 17 | } 18 | 19 | useEffect(() => { 20 | let expandStatus 21 | if (list) { 22 | expandStatus = list.getExpandStatus() 23 | } else if (form) { 24 | expandStatus = propExpandStatus || 'collapse' 25 | form.setFormState(state => state.expandStatus = expandStatus) 26 | } 27 | 28 | setDisplay(expandStatus === 'expand') 29 | }, []) 30 | 31 | useEffect(() => { 32 | const fnRef = form.subscribe(({ type }) => { 33 | if (type === ListLifeCycleTypes.ON_LIST_FILTER_ITEM_EXPAND) { 34 | setDisplay(true) 35 | } else if (type === ListLifeCycleTypes.ON_LIST_FILTER_ITEM_COLLAPSE) { 36 | setDisplay(false) 37 | } 38 | }) 39 | return function cleanup() { 40 | form.unsubscribe(fnRef) 41 | } 42 | }, [form]) 43 | } 44 | 45 | export default useExpandContainer 46 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useFilter.ts: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect, useRef } from 'react' 2 | import ListContext from '../context' 3 | import { ListLifeCycleTypes, IList } from '@alist/core' 4 | import useForceUpdate from '../hooks/useForceUpdate' 5 | import { IFilterHook, IFilterProps } from '../types' 6 | 7 | export const useFilter = (props: IFilterProps, propsList?: IList): IFilterHook => { 8 | const filterRef = useRef(props.form || null) 9 | const { useForm, effects, mirror } = props 10 | const list = propsList || useContext(ListContext) 11 | const filterProps = list.getFilterProps() 12 | const latestInstance = list.getFilterInstance() 13 | const mirrorProps: any = mirror ? { value: latestInstance.getFormState(state => state.values) } : {} 14 | 15 | const filterInstance = useForm({ 16 | ...mirrorProps, 17 | ...props, 18 | ...filterProps, 19 | form: filterRef.current, 20 | effects: list.getFilterEffects({ effects: mirror ? latestInstance.originalEffects : effects }), 21 | }) 22 | 23 | if (mirror && !filterRef.current) { 24 | list.appendMirrorFilterInstance(filterInstance) 25 | } 26 | 27 | filterRef.current = filterRef.current || filterInstance 28 | if (!latestInstance) { 29 | filterInstance.originalEffects = effects 30 | list.setFilterInstance(filterInstance) 31 | } 32 | 33 | useEffect(() => { 34 | if (mirror) { 35 | const idMirror = filterInstance.subscribe(({ type, payload }) => { 36 | if (type === 'onFieldChange') { 37 | const fieldState = payload.getState() 38 | const { name, value, props, values, editable } = fieldState 39 | latestInstance.setFieldState(name, state => { 40 | state.value = value 41 | state.values = values 42 | state.editable = editable 43 | state.props = props 44 | }) 45 | } 46 | }) 47 | return function cleanup() { 48 | filterInstance.unsubscribe(idMirror) 49 | } 50 | } 51 | }, []) 52 | 53 | const forceUpdate = useForceUpdate() 54 | const refresh = () => { 55 | forceUpdate() 56 | } 57 | 58 | useEffect(() => { 59 | const id = list.subscribe(ListLifeCycleTypes.ON_LIST_FILTER_REFRESH, refresh) 60 | return function cleanup() { 61 | list.unSubscribe(id) 62 | } 63 | }, []) 64 | 65 | return { 66 | list, 67 | filterInstance, 68 | } 69 | } 70 | 71 | export default useFilter 72 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useFilterItem.ts: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect } from 'react' 2 | import ListContext from '../context' 3 | import { ListLifeCycleTypes } from '@alist/core' 4 | import useForceUpdate from './useForceUpdate' 5 | import { IFilterItemProps, IFilterItemHook } from '../types' 6 | 7 | const getFormatName = (name) => { 8 | return name 9 | } 10 | 11 | const getValidateConfigByName = (validateConfig: any, name: string) => { 12 | // TODO: 对象表单 13 | // TODO: 数组类表单 14 | return validateConfig[name] 15 | } 16 | 17 | export const useFilterItem = (props: IFilterItemProps): IFilterItemHook => { 18 | const { name } = props 19 | const list = useContext(ListContext) 20 | const formatName = getFormatName(name) 21 | const formValidateConfig = list.getValidateConfig() 22 | const validateConfig = getValidateConfigByName(formValidateConfig, formatName) 23 | 24 | const forceUpdate = useForceUpdate() 25 | const refresh = (names) => { 26 | if (names === '*' || names.some(n => n === name)) { 27 | forceUpdate() 28 | } 29 | } 30 | 31 | useEffect(() => { 32 | const id = list.subscribe(ListLifeCycleTypes.ON_LIST_FILTER_REFRESH, refresh) 33 | return function cleanup() { 34 | list.unSubscribe(id) 35 | } 36 | }, []) 37 | 38 | 39 | return { 40 | list, 41 | validateConfig, 42 | } 43 | } 44 | 45 | export default useFilterItem 46 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useForceUpdate.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from 'react'; 2 | 3 | // Returning a new object reference guarantees that a before-and-after 4 | // equivalence check will always be false, resulting in a re-render, even 5 | // when multiple calls to forceUpdate are batched. 6 | 7 | export default function useForceUpdate(): () => void { 8 | const [ , dispatch ] = useState<{}>(Object.create(null)); 9 | 10 | // Turn dispatch(required_parameter) into dispatch(). 11 | const memoizedDispatch = useCallback( 12 | (): void => { 13 | dispatch(Object.create(null)); 14 | }, 15 | [ dispatch ], 16 | ); 17 | return memoizedDispatch; 18 | } -------------------------------------------------------------------------------- /packages/react/src/hooks/useList.ts: -------------------------------------------------------------------------------- 1 | import { useRef, useMemo, useContext, useEffect } from 'react' 2 | import createList, { ListLifeCycle, ListLifeCycleTypes } from '@alist/core' 3 | import { IList } from '@alist/core/lib/types' 4 | import ListDomain from '../context/listDomain' 5 | import { createListEffects, createListActions, useEva } from '../shared' 6 | import { IListUIProps } from '../types' 7 | 8 | export const useList = (options: IListUIProps): IList & { actions: any } => { 9 | const optionsRef = useRef(options) 10 | const actionsRef = useRef(options.actions) 11 | const listDomain = useContext(ListDomain) 12 | actionsRef.current = actionsRef.current || createListActions() 13 | 14 | // 延迟实现 15 | const { implementActions, dispatch } = useEva({ 16 | actions: actionsRef.current, 17 | effects: createListEffects(options.effects, actionsRef.current) 18 | }) 19 | 20 | const lifeCycles = [ 21 | new ListLifeCycle( 22 | ({ type, payload }) => { 23 | dispatch.lazy(type, () => payload) 24 | } 25 | ), 26 | new ListLifeCycle( 27 | ListLifeCycleTypes.ON_LIST_WILL_INIT, 28 | ({ payload, ctx }) => { 29 | const actions = { 30 | ...ctx, 31 | dispatch: ctx.notify 32 | } 33 | 34 | implementActions(actions) 35 | actionsRef.current.addAPI = (name, fn) => { 36 | actionsRef.current[name] = fn 37 | } 38 | 39 | if (listDomain) { 40 | listDomain.setContext({ actions }) 41 | } 42 | } 43 | ) 44 | ] 45 | 46 | optionsRef.current.lifeCycles = lifeCycles 47 | const alreadyHaveList = !!optionsRef.current.list 48 | const list = useMemo(() => { 49 | const originList = alreadyHaveList ? optionsRef.current.list : createList(optionsRef.current) 50 | return { 51 | ...originList, 52 | actions: actionsRef.current, 53 | } 54 | }, []) 55 | 56 | const { dataSource } = options || {} 57 | useEffect(() => { 58 | if ('dataSource' in options) { 59 | list.setDataSource(dataSource) 60 | } 61 | }, [dataSource]) 62 | 63 | // 这里是为了在next/antd/其他顶层去实现搜索区域 64 | if (optionsRef.current.afterInitialized) { 65 | optionsRef.current.afterInitialized(list) 66 | } 67 | 68 | return list 69 | } 70 | 71 | export default useList 72 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useListDomain.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react' 2 | import { ListDomain } from '../shared' 3 | 4 | export const useListDomain = () => { 5 | const listDomain = useMemo(() => { 6 | return new ListDomain() 7 | }, []) 8 | return listDomain 9 | } 10 | 11 | export default useListDomain 12 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useLoading.ts: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect } from 'react' 2 | import ListContext from '../context' 3 | import MultipleContext from '../context/multiple' 4 | import { ListLifeCycleTypes, IList } from '@alist/core' 5 | import useForceUpdate from './useForceUpdate' 6 | import { ITableProps, ILoadingHook } from '../types' 7 | 8 | export const useLoading = (props: ITableProps = {}, propList?: IList): ILoadingHook => { 9 | const { multipleId: propsMultipleId } = props 10 | const list = propList || useContext(ListContext) 11 | const { id: contextMultipleId } = useContext(MultipleContext) || {} 12 | const multipleId = propsMultipleId || contextMultipleId 13 | const loading = list ? list.getLoading() : props.loading 14 | 15 | const forceUpdate = useForceUpdate() 16 | const refresh = (opts) => { 17 | const { payload } = opts; 18 | const { notifyId } = payload || {} 19 | if (notifyId) { 20 | if (multipleId !== undefined) { 21 | if (notifyId && notifyId.some(id => id === multipleId)) { 22 | forceUpdate() 23 | } 24 | } else { 25 | forceUpdate() 26 | } 27 | } else { 28 | forceUpdate() 29 | } 30 | } 31 | 32 | useEffect(() => { 33 | if (list) { 34 | const id = list.subscribe(ListLifeCycleTypes.ON_LIST_LOADING_REFRESH, refresh) 35 | return function cleanup () { 36 | list.unSubscribe(id) 37 | } 38 | } 39 | }, [list]) 40 | 41 | return { 42 | loading, 43 | } 44 | } 45 | 46 | export default useLoading 47 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useMutlipleProvider.ts: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect } from 'react' 2 | import ListContext from '../context/' 3 | import { ListLifeCycleTypes } from '@alist/core' 4 | import useForceUpdate from '../hooks/useForceUpdate' 5 | import { IMultipleProps, IMultipleHook } from '../types' 6 | 7 | export const useMultipleProvider = (props: IMultipleProps): IMultipleHook => { 8 | const { id, pageSize = 10 } = props 9 | const list = useContext(ListContext) 10 | 11 | useEffect(() => { 12 | list && list.setMultiplePageSize({ [id]: pageSize }) 13 | }, []) 14 | 15 | const forceUpdate = useForceUpdate() 16 | const refresh = () => { 17 | forceUpdate() 18 | } 19 | 20 | useEffect(() => { 21 | if (list) { 22 | const id = list.subscribe(ListLifeCycleTypes.ON_LIST_MULTIPLE_REFRESH, refresh) 23 | return function cleanup() { 24 | list.unSubscribe(id) 25 | } 26 | } 27 | }, [list]) 28 | 29 | return { 30 | id, 31 | pageSize, 32 | list, 33 | } 34 | } 35 | 36 | export default useMultipleProvider 37 | -------------------------------------------------------------------------------- /packages/react/src/hooks/usePagination.ts: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect } from 'react' 2 | import ListContext from '../context' 3 | import ListPropsContext from '../context/listProps' 4 | import MultipleContext from '../context/multiple' 5 | import { ListLifeCycleTypes, IListKVMap, IListResponse, IList } from '@alist/core' 6 | import useForceUpdate from './useForceUpdate' 7 | import { IPaginationProps, IPaginationHook } from '../types' 8 | 9 | export const usePagination = (props: IPaginationProps = {}, propList?: IList): IPaginationHook => { 10 | const { multipleId: propsMultipleId } = props 11 | const list = propList || useContext(ListContext) 12 | const { id: contextMultipleId } = useContext(MultipleContext) || {} 13 | const multipleId = propsMultipleId || contextMultipleId 14 | const listProps = useContext(ListPropsContext) || {} 15 | let hideWhenInvalid = listProps.hideWhenInvalid || false 16 | 17 | let pageData 18 | let setCurrentPage 19 | // 多实例列表的跳转页面通过setMultipleData实现 20 | if (multipleId !== undefined) { 21 | const multipleData = list.getMultipleData() 22 | const { dataList, ...others } = multipleData[multipleId] as IListKVMap || {} 23 | setCurrentPage = (currentPage: number) => list.setMultipleData({ [multipleId]: { currentPage }}) 24 | pageData = { ...others } 25 | // 常规的跳转页面方法 26 | } else { 27 | pageData = list.getPageData() 28 | setCurrentPage = list.setCurrentPage 29 | } 30 | 31 | const forceUpdate = useForceUpdate() 32 | const refresh = (opts) => { 33 | const { payload } = opts; 34 | const { notifyId } = payload || {} 35 | if (notifyId) { 36 | if (multipleId !== undefined) { 37 | if (notifyId && notifyId.some(id => id === multipleId)) { 38 | forceUpdate() 39 | } 40 | } else { 41 | forceUpdate() 42 | } 43 | } else { 44 | forceUpdate() 45 | } 46 | } 47 | 48 | useEffect(() => { 49 | const id = list.subscribe(ListLifeCycleTypes.ON_LIST_PAGINATION_REFRESH, refresh) 50 | return function cleanup () { 51 | list.unSubscribe(id) 52 | } 53 | }, [list]) 54 | 55 | return { 56 | list, 57 | pageData, 58 | setCurrentPage, 59 | setPageSize: list.setPageSize, 60 | hideWhenInvalid, 61 | } 62 | } 63 | 64 | export default usePagination 65 | -------------------------------------------------------------------------------- /packages/react/src/hooks/useTable.ts: -------------------------------------------------------------------------------- 1 | import { useContext, useEffect, useMemo } from 'react' 2 | import ListContext from '../context' 3 | import ListPropsContext from '../context/listProps' 4 | import MultipleContext from '../context/multiple' 5 | import { ListLifeCycleTypes, IListKVMap, IListResponse, IList } from '@alist/core' 6 | import useForceUpdate from './useForceUpdate' 7 | import { ITableProps, ITableHook } from '../types' 8 | 9 | export const useTable = (props: ITableProps = {}, propList?: IList): ITableHook => { 10 | const { pickInitialTableProps, multipleId: propsMultipleId } = props 11 | const list = propList || useContext(ListContext) 12 | const { id: contextMultipleId } = useContext(MultipleContext) || {} 13 | const listProps = useContext(ListPropsContext) || {} 14 | const multipleId = propsMultipleId || contextMultipleId 15 | const loading = list ? list.getLoading() : props.loading 16 | let hideWhenInvalid = listProps.hideWhenInvalid || false 17 | let dataSource: any[] 18 | 19 | let primaryKey: any 20 | 21 | useMemo(() => { 22 | // 初始化加载时收集tableProps相关信息 23 | if (typeof pickInitialTableProps === 'function') { 24 | const initialTableProps = pickInitialTableProps(props) 25 | primaryKey = initialTableProps.primaryKey 26 | list && list.setTableProps(initialTableProps) 27 | } 28 | }, []) 29 | 30 | // 多列表实例模式 31 | if (multipleId !== undefined) { 32 | const multipleData = list.getMultipleData() 33 | const { paginationDataList } = multipleData[multipleId] as IListKVMap || {} 34 | dataSource = paginationDataList as any [] || [] 35 | } else { 36 | if (list) { 37 | dataSource = list.getPaginationDataSource() 38 | } else { 39 | dataSource = props.dataSource 40 | } 41 | } 42 | 43 | const forceUpdate = useForceUpdate() 44 | const refresh = (opts) => { 45 | const { payload } = opts; 46 | const { notifyId } = payload || {} 47 | if (notifyId) { 48 | if (multipleId !== undefined) { 49 | if (notifyId && notifyId.some(id => id === multipleId)) { 50 | forceUpdate() 51 | } 52 | } else { 53 | forceUpdate() 54 | } 55 | } else { 56 | forceUpdate() 57 | } 58 | } 59 | 60 | useEffect(() => { 61 | if (list) { 62 | const id = list.subscribe(ListLifeCycleTypes.ON_LIST_TABLE_REFRESH, refresh) 63 | return function cleanup () { 64 | list.unSubscribe(id) 65 | } 66 | } 67 | }, [list]) 68 | 69 | const tableProps = list ? list.getTableProps() : {} 70 | 71 | return { 72 | tableProps, 73 | list, 74 | dataSource, 75 | loading, 76 | primaryKey, 77 | hideWhenInvalid, 78 | } 79 | } 80 | 81 | export default useTable 82 | -------------------------------------------------------------------------------- /packages/react/src/index.ts: -------------------------------------------------------------------------------- 1 | import ListProvider from './components/ListProvider' 2 | import FilterProvider from './components/FilterProvider' 3 | import FieldProvider from './components/FieldProvider' 4 | import TableProvider from './components/TableProvider' 5 | import PaginationProvider from './components/PaginationProvider' 6 | import MultipleProvider from './components/MultipleProvider' 7 | import LoadingProvider from './components/LoadingProvider' 8 | import ExpandTrigger from './components/ExpandTrigger' 9 | import createExpandContainer from './components/ExpandContainer' 10 | import Selection from './components/Selection' 11 | import SorterProvider from './components/SorterProvider' 12 | import ConnectProvider from './components/ConnectProvider' 13 | import ToggleProvider from './components/ToggleProvider' 14 | import Consumer from './components/Consumer' 15 | import ListContext from './context/index' 16 | import ToggleContext from './context/toggle' 17 | import ListDomainContext from './context/listDomain' 18 | import Toggle from './components/Toggle' 19 | 20 | import { 21 | createListActions, 22 | createAsyncListActions, 23 | mergeActions, 24 | createAsyncActions, 25 | createActions, 26 | useEva, 27 | ListEffectHooks, 28 | createListEffectHook, 29 | createListEffects, 30 | ListDomain 31 | } from './shared' 32 | 33 | export * from './components/Layout' 34 | export * from '@alist/core' 35 | export * from './hooks' 36 | 37 | export { 38 | ListContext, 39 | Toggle, 40 | ToggleContext, 41 | ListDomainContext, 42 | ToggleProvider, 43 | ExpandTrigger, 44 | createExpandContainer, 45 | ListProvider, 46 | FilterProvider, 47 | FieldProvider, 48 | TableProvider, 49 | PaginationProvider, 50 | ConnectProvider, 51 | MultipleProvider, 52 | LoadingProvider, 53 | SorterProvider, 54 | Consumer, 55 | Selection, 56 | mergeActions, 57 | createActions, 58 | createAsyncActions, 59 | useEva, 60 | createListActions, 61 | createListEffects, 62 | createAsyncListActions, 63 | ListEffectHooks, 64 | createListEffectHook, 65 | ListDomain 66 | } 67 | -------------------------------------------------------------------------------- /packages/react/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { IListProps } from '@alist/core/lib/types' 2 | const isFn = (fn: any) => typeof fn === 'function' 3 | 4 | const pickupAttrs = (props: any): IListProps & { actions: any } => { 5 | const { 6 | actions, 7 | dataSource, url, method, params, pageSize, currentPage, 8 | total, autoLoad, defaultFilterValues, multiple, 9 | filterConfig, query, 10 | formatBefore, formatAfter, formatFilter, 11 | lifeCycles, 12 | } = props; 13 | return { 14 | actions, 15 | dataSource, url, method, params, pageSize, currentPage, 16 | total, autoLoad, defaultFilterValues, multiple, 17 | filterConfig, query, 18 | formatBefore, formatAfter, formatFilter, 19 | lifeCycles, 20 | } 21 | } 22 | 23 | const normalizeNumPx = (numpx) => { 24 | return `${numpx}`.replace('px', '') 25 | } 26 | 27 | const isValidNumber = (num) => { 28 | const normalizeNum = Number(normalizeNumPx(num)) 29 | return !isNaN(normalizeNum) && !!normalizeNum 30 | } 31 | 32 | export { 33 | isFn, 34 | pickupAttrs, 35 | normalizeNumPx, 36 | isValidNumber, 37 | }; 38 | -------------------------------------------------------------------------------- /packages/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./lib" 5 | }, 6 | "include": [ 7 | "./src/**/*.ts", 8 | "./src/**/*.tsx" 9 | ], 10 | "exclude": [ 11 | "./src/__tests__/*" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /scripts/build.ts: -------------------------------------------------------------------------------- 1 | import ts from 'typescript' 2 | 3 | function exit(exitCode: number) { 4 | if (exitCode === 0) return 5 | console.log(`Process exiting with code '${exitCode}'.`) 6 | process.exit(exitCode) 7 | } 8 | 9 | function diagnositcReporter(diagnostic: ts.Diagnostic) { 10 | let msg = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); 11 | if (diagnostic.file) { 12 | const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start); 13 | msg = `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${msg}`; 14 | } 15 | console.error(msg); 16 | } 17 | 18 | export function getCompileConfig(configPath: string, extendOptions?: ts.CompilerOptions) { 19 | const host: ts.ParseConfigFileHost = ts.sys as any 20 | host.onUnRecoverableConfigFileDiagnostic = diagnositcReporter 21 | const parsedCmd = ts.getParsedCommandLineOfConfigFile(configPath, extendOptions, host); 22 | host.onUnRecoverableConfigFileDiagnostic = undefined 23 | if (parsedCmd.errors.length) { 24 | console.error(parsedCmd.errors.join('\n')) 25 | exit(1) 26 | } 27 | return { options: parsedCmd.options, fileNames: parsedCmd.fileNames } 28 | } 29 | 30 | 31 | export function compile(rootNames: string[], options: ts.CompilerOptions, customTransformers?: ts.CustomTransformers) { 32 | const program = ts.createProgram({ rootNames, options }) 33 | const emitResult = program.emit( 34 | undefined, 35 | undefined, 36 | undefined, 37 | undefined, 38 | customTransformers 39 | ) 40 | ts.getPreEmitDiagnostics(program) 41 | .concat(emitResult.diagnostics) 42 | .forEach(diagnositcReporter); 43 | 44 | const exitCode = emitResult.emitSkipped ? 1 : 0; 45 | exit(exitCode) 46 | } 47 | 48 | export function build(configPath: string, customTransformers?: ts.CustomTransformers) { 49 | const { options, fileNames } = getCompileConfig(configPath) 50 | compile(fileNames, options, customTransformers) 51 | } -------------------------------------------------------------------------------- /scripts/doc-renderer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import SiteRenderer from 'react-site-renderer' 3 | 4 | export default ({ docs }) => { 5 | return ( 6 | 12 | } 13 | docs={docs} 14 | /> 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /scripts/docs.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs-extra') 3 | const { command } = require('doc-scripts') 4 | const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin') 5 | const HEAD_HTML = ` 6 | 26 | ` 27 | 28 | const createDocs = async () => { 29 | const packagesDir = path.resolve(process.cwd(), './packages') 30 | const packages = await fs.readdir(packagesDir) 31 | const alias = packages 32 | .map(v => path.join(packagesDir, v)) 33 | .filter(v => { 34 | return !fs.statSync(v).isFile() 35 | }) 36 | .reduce((buf, _path) => { 37 | const name = path.basename(_path) 38 | 39 | return { 40 | ...buf, 41 | [`@alist/${name}`]: `${_path}/src` 42 | } 43 | }, {}) 44 | command( 45 | { 46 | title: 'AList', 47 | renderer: path.resolve(__dirname, './doc-renderer.js'), 48 | header: HEAD_HTML, 49 | }, 50 | (webpackConfig, env) => { 51 | webpackConfig.devtool = 'none' 52 | webpackConfig.module.rules = webpackConfig.module.rules.map(rule => { 53 | if (rule.test.test('.tsx')) { 54 | return { 55 | ...rule, 56 | use: [ 57 | { 58 | loader: require.resolve('ts-loader'), 59 | options: { 60 | transpileOnly: true 61 | } 62 | } 63 | ] 64 | } 65 | } else { 66 | return rule 67 | } 68 | }) 69 | 70 | Object.assign(webpackConfig.resolve.alias, { 71 | ...alias, 72 | '@alifd/next': path.resolve( 73 | __dirname, 74 | '../packages/next/node_modules/@alifd/next' 75 | ), 76 | antd: path.resolve(__dirname, '../packages/antd/node_modules/antd'), 77 | '@formily/next': path.resolve( 78 | __dirname, 79 | '../packages/next/node_modules/@formily/next' 80 | ), 81 | '@formily/next-components': path.resolve( 82 | __dirname, 83 | '../packages/next/node_modules/@formily/next-components' 84 | ), 85 | '@formily/antd-components': path.resolve( 86 | __dirname, 87 | '../packages/antd/node_modules/@formily/antd-components' 88 | ), 89 | '@formily/antd': path.resolve( 90 | __dirname, 91 | '../packages/antd/node_modules/@formily/antd' 92 | ), 93 | }) 94 | webpackConfig.resolve.plugins = [ 95 | new TsconfigPathsPlugin({ 96 | configFile: path.resolve(__dirname, '../tsconfig.json') 97 | }) 98 | ] 99 | return webpackConfig 100 | } 101 | ) 102 | } 103 | createDocs() 104 | -------------------------------------------------------------------------------- /scripts/global.js: -------------------------------------------------------------------------------- 1 | import prettyFormat from 'pretty-format' 2 | 3 | global.prettyFormat = prettyFormat 4 | 5 | global.sleep = time => { 6 | return new Promise(resolve => setTimeout(resolve, time)) 7 | } 8 | 9 | global.requestAnimationFrame = fn => setTimeout(fn) 10 | -------------------------------------------------------------------------------- /scripts/jest.base.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra') 2 | const path = require('path') 3 | const packagesDir = path.resolve(process.cwd(), './packages') 4 | const packages = fs.readdirSync(packagesDir) 5 | const alias = packages 6 | .map(v => path.join(packagesDir, v)) 7 | .filter(v => { 8 | return !fs.statSync(v).isFile() 9 | }) 10 | .reduce((buf, _path) => { 11 | const name = path.basename(_path) 12 | return { 13 | ...buf, 14 | [`@alist/${name}`]: `${_path}/src` 15 | } 16 | }, {}) 17 | module.exports = { 18 | collectCoverage: true, 19 | verbose: true, 20 | testEnvironment: 'jsdom', 21 | transform: { 22 | '^.+\\.jsx?$': 'babel-jest' 23 | }, 24 | preset: 'ts-jest', 25 | setupFilesAfterEnv: [ 26 | require.resolve('jest-dom/extend-expect'), 27 | require.resolve('@testing-library/react/cleanup-after-each'), 28 | './scripts/global.js' 29 | ], 30 | moduleNameMapper: process.env.TEST_ENV === 'production' ? undefined : alias, 31 | globals: { 32 | 'ts-jest': { 33 | babelConfig: true, 34 | tsConfig: 'tsconfig.jest.json' 35 | } 36 | }, 37 | //watchPlugins: ['jest-watch-lerna-packages'], 38 | coveragePathIgnorePatterns: [ 39 | '/node_modules/', 40 | 'package.json', 41 | '/demo/', 42 | '/packages/builder/src/__tests__/', 43 | '/packages/builder/src/components/', 44 | '/packages/builder/src/configs/', 45 | 'package-lock.json' 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "moduleResolution": "node", 5 | "allowJs": true, 6 | "module": "commonjs", 7 | "target": "es5", 8 | } 9 | } -------------------------------------------------------------------------------- /tsconfig.jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "jsx": "react" 5 | } 6 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "moduleResolution": "node", 5 | "jsx": "react", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "allowJs": false, 9 | "noUnusedLocals": true, 10 | "removeComments": true, 11 | "preserveConstEnums": true, 12 | "sourceMap": false, 13 | "declaration": true, 14 | "baseUrl": "." 15 | } 16 | } 17 | --------------------------------------------------------------------------------