mirror of
https://gitee.com/ant-design-vue/ant-design-vue.git
synced 2024-11-29 18:48:32 +08:00
Feat 1.5.0 (#1853)
* feat: add Result component * fix: update md template tag html>tpl - fix `result` typo - update jest `result` snapshots * refactor: svg file to functional component icon - update jest snapshot * feat: add result * Feat descriptions (#1251) * feat: add descriptions * fix: add descriptions types and fix docs * fix: lint change code * fix: demo warning * fix: update demo, snapshot and remove classnames * test: add descriptions test * fix: descriptions demo (#1498) * feat: add page header (#1250) * feat: add page-header component * update site: page-header * ts definition update: page-header * get page-header props with getComponentFromProp func * optimize page-header * doc: add page-header actions.md responsive.md * breadcrumb itemRender add pure function support * style: format code * feat: update style to 3.23.6 from 2.13.6 * feat: update style to 3.26.8 from 3.23.6 * chore: update util * chore: update util * feat: update affix * feat: update alert * feat: update anchor * feat: update auto-complete * feat: update avatar * feat: update back-top * feat: update badge * feat: update button * feat: update breadcrumb * feat: update ts * docs: update doc * feat: update calendat * feat: update card * feat: update carousel * feat: update carousel * feat: update checkbox * feat: update comment * feat: update config-provider * docs: update doc * feat: update collapse * feat: update locale * feat: update date-picker * feat: update divider * feat: update drawer * feat: update dropdown * feat: update rc-trigger * feat: update dropdown * feat: update empty * test: add empty test * feat: update form * feat: update form * feat: update spin * feat: update grid * docs: update grid doc * feat: update icon * feat: update slider * feat: update textarea * feat: update input-number * feat: update layout * feat: update list * feat: update menu * feat: meaage add key for update content * feat: modal add closeIcon support * feat: update notification * feat: add pagination disabled support * feat: popconfirm add disabled support * test: update popover * feat: progress support custom line-gradiend * feat: update radio * test: update radio test * docs: update rate demo * feat: skeleton add avatar support number type * test: add switch test * test: update statistic test * fix: input clear icon event * feat: steps add type、 v-model、subTitle * feat: delete typography component * feat: delete Typography style * perf: update select * feat: add download transformFile previewFile actio * docs: update upload * feat: update tree-select * docs: update tree-select * feat: tree add blockNode selectable * docs: add tree demo * test: update snap * docs: updatedoc * feat: update tag * docs: update ad doc * feat: update tooltip * feat: update timeline * feat: time-picker add clearIcon * docs: update tabs * feat: transfer support custom children * test: update transfer test * feat: update table * test: update table test * test: update test * feat: calendar update locale * test: update test snap * feat: add mentions (#1790) * feat: mentions style * feat: theme default * feat: add mentions component * feat: mentions API * feat: add unit test for mentions * feat: update mentions demo * perf: model and inheritAttrs for mentions * perf: use getComponentFromProp instead of this.$props * perf: mentions rm defaultProps * feat: rm rows in mentionsProps * fix: mentions keyDown didn't work * docs: update mentions api * perf: mentions code * feat: update mentions * bump 1.5.0-alpha.1 * feat: pageheader add ghost prop * docs: update descriptions demo * chore: page-header add ghost type * fix: color error * feat: update to 3.26.12 * fix: some prop default value * fix(typo): form, carousel, upload. duplicate identifier (#1848) * Add Mentions Type (#1845) * feat: add mentions type * feat: add mentions in ant-design-vue.d.ts * docs: update doc * docs: add changelog * fix: mentions getPopupCotainer value (#1850) * docs: update doc * docs: uptate demo * docs: update demo * docs: delete demo * docs: delete doc * test: update snapshots * style: format code * chore: update travis * docs: update demo Co-authored-by: Sendya <18x@loacg.com> Co-authored-by: zkwolf <chenhao5866@gmail.com> Co-authored-by: drafish <xwlyy1991@163.com> Co-authored-by: Amour1688 <31695475+Amour1688@users.noreply.github.com>
This commit is contained in:
parent
ae0590d794
commit
73bef787cd
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "antdv-demo"]
|
||||
path = antdv-demo
|
||||
url = https://github.com/tangjinzhou/antdv-demo.git
|
@ -24,6 +24,6 @@ components/style/color/*.less
|
||||
.gitattributes
|
||||
.stylelintrc
|
||||
.vcmrc
|
||||
logo.png
|
||||
.png
|
||||
.npmrc.template
|
||||
.huskyrc
|
||||
|
@ -1,7 +1,12 @@
|
||||
language: node_js
|
||||
sudo: required
|
||||
git:
|
||||
submodules: false
|
||||
node_js:
|
||||
- 12.4.0
|
||||
before_install:
|
||||
- echo -e "[submodule "antdv-demo"]\n path = antdv-demo\n https://$GITHUB_TOKEN@github.com/tangjinzhou/antdv-demo.git" >~/.gitmodules
|
||||
- git submodule update --init --recursive
|
||||
before_script:
|
||||
- npm install vue vue-template-compiler
|
||||
script:
|
||||
@ -9,7 +14,6 @@ script:
|
||||
- COVERAGE=true npm run test
|
||||
- npm run codecov
|
||||
- if [[ $TRAVIS_BRANCH == "master" && $TRAVIS_PULL_REQUEST == "false" ]]; then npm run pub-with-ci; fi
|
||||
- bash ./scripts/deploy-to-gh-pages.sh
|
||||
env:
|
||||
matrix:
|
||||
secure: PBbJaS48HA/mkj9PuGuRxs00DEJR77XfuPdSlTvCq0QxLIR6wIO+t3LLJdOQctZIX6KWBR/Zq3zSn5bRxgPIaRcoyuEU25ga4cexJMEh1ymE23uTiDcnWwWN0X1jZKGuHPvqVKjyToAv6XW24mTXNvEAqD2uL101JxBseoWJ/2VtyOjJFJwcGbw+MTLymWCZiAF10w+k0SyigawaxZLlYL9LZXv4w3oCjCwuiTD/T6rvyT3wGQzXx7/P7XQGL4el4lE7leuK5m2PhWvX2S3t2FRpoZPw0DINJu5XzuBr3DSMErQjCrP4Ep8iqW8pGGLkoXbcxK3/K+uSy0k+DdBN7jRgnnOeLpqeVUSMaM6LRnl2XyDWL3dKpVbEzZaFkRTmAwdbgYjI+7Enn3/GtseMASo/gK47m2k+kE/msoqwpTGLC5DBOBKxdNShdFnEbOxLUUiVNgoZRXbj6VhdueqK89LsMDsnxzmFtrU8Ytgv8wJsFd5IkIhCStmQ9bdTqER659hd1Qqdh6Qe36AfpZcetOLr86Z++CSwA/pZbLPeEVrfCHDh6V3DPQXG+Zlf/m60OAmhosJ+4dxZwRnR8LnaDFZ+uLYMz+vJGeOtFHvczz7TW4mznjguLE51crG+mkBGT2dx1UUg7zs41lz3GtH9WY8cSG4y5ryjDl6YkXwoiZI=
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
<h2 align="center">支付宝/微信</h2>
|
||||
|
||||
### 使用支付宝/微信的赞助的用户,如需要添加名单,赞助后可发 github 账号到邮箱(415800467@qq.com)
|
||||
### 使用支付宝/微信的赞助的用户,如需要添加名单,赞助后可发 github 账号到邮箱(antdv@foxmail.com)
|
||||
|
||||
- [fastgoo](https://github.com/fastgoo)
|
||||
- [sendya](https://github.com/sendya)
|
||||
|
@ -4,12 +4,85 @@
|
||||
|
||||
#### Release Schedule
|
||||
|
||||
- Weekly release: patch version at the end of every week for routine bugfix (anytime for urgent bugfix).
|
||||
- Weekly release: patch version for routine bugfix.
|
||||
- Monthly release: minor version for new features.
|
||||
- Major version release is not included in this schedule for breaking change and new features.
|
||||
|
||||
---
|
||||
|
||||
## 1.5.0
|
||||
|
||||
`2020-03-06`
|
||||
|
||||
### Component features and styles are synchronized to antd version 3.26.12.
|
||||
|
||||
- Four new components have been added:
|
||||
- 🔥🔥🔥 [Mentions](https://antdv.com/components/mentions-cn/) Added mentioned components and discarded the original Mention components.
|
||||
- 🔥🔥🔥 [Descriptions](https://antdv.com/components/descriptions-cn/) Display multiple read-only fields in groups.
|
||||
- 🔥🔥🔥 [PageHeader](https://antdv.com/components/page-header-cn/) can be used to declare the topic of the page, display important information about the page that the user is concerned about, and carry the operation items related to the current page.
|
||||
- 🔥🔥🔥 [Result](https://antdv.com/components/result) is used to feedback the processing results of a series of operation tasks.
|
||||
- 🔥 Descriptions supports vertical layout.
|
||||
- 🔥 Progress.Circle supports gradient colors.
|
||||
- 🔥 Progress.Line supports gradient colors.
|
||||
- Breadcrumb
|
||||
- 🎉 Breadcrumb.Item supports the `overlay` property to define drop-down menus.
|
||||
- 🌟 Added `Breadcrumb.Separator` component, you can customize`separator`.
|
||||
- 🌟 TreeSelect's `showSearch` supports multiple selection mode.
|
||||
- 🌟 Timeline.Item adds `gray` color type, which can be used in incomplete or invalid state.
|
||||
- 🌟 Modal supports `closeIcon` property for customizing the close icon.
|
||||
- Upload
|
||||
- 🌟 Upload provides `previewFile` property to customize the preview logic.
|
||||
- 🌟 Upload adds `transformFile` to support converting files before uploading.
|
||||
- 🌟 Upload supports previewing pictures in jfif format.
|
||||
- 🌟 Added `showDownloadIcon` property for displaying download icons.
|
||||
- 🌟 Input.Search adds `loading` property, which is used to display the loading status.
|
||||
- 🌟 Grid's `gutter` property adds support for vertical spacing. Now you can set an array for`gutter`, the second value of the array represents the vertical spacing.
|
||||
- 🌟 message Added support for updating content with unique `key`.
|
||||
- 🌟 TextArea supports `allowClear`.
|
||||
- 🌟 Dropdown.Button supports `icon` property to customize the icon.
|
||||
- Drawer
|
||||
- 🌟 Support `afterVisibleChange` property, which is triggered after the drawer animation is completed.
|
||||
- 🌟 Support `ESC` shutdown.
|
||||
- 🌟 Added `keyboard`, which allows the response to keyboard events to be turned on and off.
|
||||
- 🌟 TreeNode supports `checkable` property.
|
||||
- 🌟 Transfer supports `children` custom rendering list.
|
||||
- 🌟 Pagination supports `disabled` property.
|
||||
- 🌟 Steps support click to switch function.
|
||||
- Slider
|
||||
- 🌟 Support `tooltipPlacement` to define the location of the tip.
|
||||
- 🌟 Support `getTooltipPopupContainer` to allow custom container for the prompt.
|
||||
- 🌟 Flip `trigger` direction when Sider is on the right.
|
||||
- 🌟 Calendar supports `headerRender` to customize header.
|
||||
- 🌟 Carousel supports custom panel pointing point locations.
|
||||
- 🌟 Collapse supports `expandIconPosition` property.
|
||||
- 🌟 Popconfirm adds `disabled` props, which are used to control whether clicking child elements pop up.
|
||||
- 🌟 Select supports `showArrow` in multi-select mode.
|
||||
- 🌟 Collapse.Panel added `extra`.
|
||||
- Card
|
||||
- 🌟 Card component added `tabBarExtraContent` property.
|
||||
- 🌟 Card.Grid added a hoverable property to allow floating effects to be disabled.
|
||||
- 🌟 Anchor.Link adds `target` attribute.
|
||||
- 🌟 TimePicker added `clearIcon` prop for custom clear icon.
|
||||
- Form
|
||||
- 🌟 Support to configure the `colon` property directly on the Form.
|
||||
- 🌟 Support `labelAlign` property.
|
||||
- Table
|
||||
- 🌟 Table adds `getPopupContainer` property for setting various floating layer rendering nodes in the table.
|
||||
- 💄 Adjust the style of the Table expand button.
|
||||
- 🌟 Added `tableLayout` property, supports setting the table's`table-layout` layout, and enables `tableLayout =" fixed "` by default under fixed headers / columns, to solve the column alignment problem caused by the table layout automatically based on content .
|
||||
- 🌟 Added `column.ellipsis` to support automatic omission of cell contents.
|
||||
- 🌟 Added `scroll.scrollToFirstRowOnChange` property, which is used to set whether to scroll to the top of the table after page turning. -Filter `filterDropdown` Added`visible` parameter to get the display status of the drop-down box.
|
||||
- 🌟 The `title` method adds a`sortColumn` parameter to get the currently sorted column. -Sort When sorting, the `sorter` parameter of`onChange` will always contain `column` information.
|
||||
- 🌟 Tree component supports `blockNode` property.
|
||||
- 🌟 RangePicker adds `separator` definition.
|
||||
- Empty
|
||||
- 🌟 Empty supports the `imageStyle` property.
|
||||
- 🌟 Empty `description` supports`false`.
|
||||
- 🌟 Empty Supports access to preset pictures via `Empty.PRESENTED_IMAGE_DEFAULT` and`Empty.PRESENTED_IMAGE_SIMPLE`
|
||||
- 🌟 Badge supports custom colors.
|
||||
- 🐞 Fix the problem that the label of Steps is not centered.
|
||||
- 🐞 Fix cursor style problem of DatePicker and TimePicker.
|
||||
|
||||
## 1.4.12
|
||||
|
||||
`2020-03-03`
|
||||
|
@ -4,12 +4,87 @@
|
||||
|
||||
#### 发布周期
|
||||
|
||||
- 修订版本号:每周末会进行日常 bugfix 更新。(如果有紧急的 bugfix,则任何时候都可发布)
|
||||
- 修订版本号:日常 bugfix 更新
|
||||
- 次版本号:带有新特性的向下兼容的版本。
|
||||
- 主版本号:含有破坏性更新和新特性,不在发布周期内。
|
||||
|
||||
---
|
||||
|
||||
## 1.5.0
|
||||
|
||||
`2020-03-06`
|
||||
|
||||
### 组件功能和样式同步到 antd 3.26.12 版本。
|
||||
|
||||
- 新增了四个组件:
|
||||
- 🔥🔥🔥 [Mentions](https://antdv.com/components/mentions-cn/) 新增提及组件并废弃原有 Mention 组件。
|
||||
- 🔥🔥🔥 [Descriptions](https://antdv.com/components/descriptions-cn/) 成组展示多个只读字段。
|
||||
- 🔥🔥🔥 [PageHeader](https://antdv.com/components/page-header-cn/) 可用于声明页面主题、展示用户所关注的页面重要信息,以及承载与当前页相关的操作项。
|
||||
- 🔥🔥🔥 [Result](https://antdv.com/components/result) 用于反馈一系列操作任务的处理结果。
|
||||
- 🔥 Descriptions 支持垂直布局。
|
||||
- 🔥 Progress.Circle 支持渐变色。
|
||||
- 🔥 Progress.Line 支持渐变色。
|
||||
- Breadcrumb
|
||||
- 🎉 Breadcrumb.Item 支持 `overlay` 属性来定义下拉菜单。
|
||||
- 🌟 新增 `Breadcrumb.Separator` 组件,可进行 `separator` 自定义。
|
||||
- 🌟 TreeSelect 的 `showSearch` 支持多选模式。
|
||||
- 🌟 Timeline.Item 新增 `gray` 色彩类型,可用于未完成或失效状态。
|
||||
- 🌟 Modal 支持 `closeIcon` 属性用于自定义关闭图标。
|
||||
- 🌟 Upload
|
||||
- 🌟 Upload 提供 `previewFile` 属性以自定义预览逻辑。
|
||||
- 🌟 Upload 新增 `transformFile` 支持上传之前转换文件。
|
||||
- 🌟 Upload 支持预览 `jfif` 格式图片。
|
||||
- 🌟 新增 `showDownloadIcon` 属性,用于展示下载图标。
|
||||
- 🌟 Input.Search 新增 `loading` 属性,用于展示加载中的状态。
|
||||
- 🌟 Grid 的 `gutter` 属性新增垂直间距的支持,现在你可以给 `gutter` 设置一个数组,数组的第二个值就表示垂直间距。
|
||||
- 🌟 message 新增支持通过唯一的 `key` 来更新内容。
|
||||
- 🌟 TextArea 支持 `allowClear`。
|
||||
- 🌟 Dropdown.Button 支持 `icon` 属性来自定义图标。
|
||||
- Drawer
|
||||
- 🌟 支持 `afterVisibleChange` 属性,在抽屉动画完成后触发。
|
||||
- 🌟 支持 `ESC` 关闭。
|
||||
- 🌟 新增 `keyboard`,允许打开关闭对键盘事件的响应。
|
||||
- 🌟 TreeNode 支持 `checkable` 属性。
|
||||
- 🌟 Transfer 支持 `children` 来自定义渲染列表。
|
||||
- 🌟 Pagination 支持 `disabled` 属性。
|
||||
- 🌟 Steps 支持点击切换功能。
|
||||
- Slider
|
||||
- 🌟 支持 `tooltipPlacement` 以定义提示所在位置。
|
||||
- 🌟 支持 `getTooltipPopupContainer` 以允许自定义提示所在容器。
|
||||
- 🌟 当 Sider 在右边时,翻转 `trigger` 方向。
|
||||
- 🌟 Calendar 支持 `headerRender` 以自定义头部。
|
||||
- 🌟 Carousel 支持自定义面板指示点的位置。
|
||||
- 🌟 Collapse 支持 `expandIconPosition` 属性。
|
||||
- 🌟 Popconfirm 增加 `disabled` props,用于控制点击子元素是否弹出。
|
||||
- 🌟 Select 在多选模式下支持 `showArrow`。
|
||||
- 🌟 Collapse.Panel 新增了 `extra`。
|
||||
- Card
|
||||
- 🌟 Card 组件新增了 `tabBarExtraContent` 属性。
|
||||
- 🌟 Card.Grid 新增 `hoverable` 属性允许禁用浮动效果。
|
||||
- 🌟 Anchor.Link 增加 `target` 属性。
|
||||
- 🌟 TimePicker 新增了 `clearIcon` prop,用于自定义清除图标。
|
||||
- Form
|
||||
- 🌟 支持直接在 Form 上面配置 `colon` 属性。
|
||||
- 🌟 支持 `labelAlign` 属性。
|
||||
- Table
|
||||
- 🌟 Table 新增 `getPopupContainer` 属性用于设置表格内的各类浮层渲染节点。
|
||||
- 💄 调整 Table 展开按钮的样式。
|
||||
- 🌟 新增 `tableLayout` 属性,支持设置表格的 `table-layout` 布局,并在固定表头/列下默认开启 `tableLayout="fixed"`,解决因为表格自动根据内容排版造成的列对齐问题。
|
||||
- 🌟 新增 `column.ellipsis` 支持单元格内容自动省略。
|
||||
- 🌟 新增 `scroll.scrollToFirstRowOnChange` 属性,用于设置在翻页后是否滚动到表格顶部。
|
||||
- 🌟 `filterDropdown` 新增 `visible` 参数,用于获取下拉框的显示状态。
|
||||
- 🌟 `title` 方法新增 `sortColumn` 参数,用于获取当前排序的列。
|
||||
- 🌟 排序时 `onChange` 的 `sorter` 参数将始终包含 `column` 信息。
|
||||
- 🌟 Tree 组件支持 `blockNode` 属性。
|
||||
- 🌟 RangePicker 添加 `separator` 定义。
|
||||
- Empty
|
||||
- 🌟 Empty 支持 `imageStyle` 属性。
|
||||
- 🌟 Empty `description` 支持 `false`。
|
||||
- 🌟 Empty 支持通过 `Empty.PRESENTED_IMAGE_DEFAULT` 和 `Empty.PRESENTED_IMAGE_SIMPLE` 访问预置图片。
|
||||
- 🌟 Badge 支持自定义颜色。
|
||||
- 🐞 修复 Steps 的 label 不居中的问题。
|
||||
- 🐞 修复 DatePicker 和 TimePicker 的 cursor 样式问题。
|
||||
|
||||
## 1.4.12
|
||||
|
||||
`2020-03-03`
|
||||
|
@ -295,11 +295,12 @@ gulp.task(
|
||||
gulp.task(
|
||||
'pub',
|
||||
gulp.series('check-git', 'compile', done => {
|
||||
if (!process.env.GITHUB_TOKEN) {
|
||||
console.log('no GitHub token found, skip');
|
||||
} else {
|
||||
pub(done);
|
||||
}
|
||||
// if (!process.env.GITHUB_TOKEN) {
|
||||
// console.log('no GitHub token found, skip');
|
||||
// } else {
|
||||
// pub(done);
|
||||
// }
|
||||
pub(done);
|
||||
}),
|
||||
);
|
||||
|
||||
|
1
antdv-demo
Submodule
1
antdv-demo
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 805d3298a8c2a6fc5deee94ad0db8cab014cd2d8
|
@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
dev: {
|
||||
componentName: 'tree', // dev components
|
||||
componentName: 'badge', // dev components
|
||||
},
|
||||
};
|
||||
|
12
build/dev.js
12
build/dev.js
@ -28,7 +28,6 @@ let { componentName } = require('./config').dev;
|
||||
const componentsInPrototype = ['Modal', 'message', 'notification'];
|
||||
|
||||
const MAIN_TEMPLATE = `import 'babel-polyfill';
|
||||
import './index.less';
|
||||
import 'highlight.js/styles/solarized-light.css';
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
@ -38,6 +37,7 @@ import VueClipboard from 'vue-clipboard2';
|
||||
import Md from './components/md';
|
||||
import Api from './components/api';
|
||||
import demoBox from './components/demoBox';
|
||||
import demoSort from './components/demoSort';
|
||||
import demoContainer from './components/demoContainer';
|
||||
import Modal from '../components/modal';
|
||||
import message from '../components/message';
|
||||
@ -47,9 +47,10 @@ import notification from '../components/notification';
|
||||
import '../components/modal/style';
|
||||
import '../components/message/style';
|
||||
import '../components/notification/style';
|
||||
import Test from '../components/{{name}}/demo/index.vue';
|
||||
import Test from '../antdv-demo/{{name}}/demo/index.vue';
|
||||
import zhCN from './theme/zh-CN';
|
||||
import enUS from './theme/en-US';
|
||||
import './index.less';
|
||||
|
||||
Vue.use(Vuex);
|
||||
Vue.use(VueClipboard);
|
||||
@ -58,6 +59,7 @@ Vue.use(VueI18n);
|
||||
Vue.component(Md.name, Md);
|
||||
Vue.component(Api.name, Api);
|
||||
Vue.component('demo-box', demoBox);
|
||||
Vue.component('demo-sort', demoSort);
|
||||
Vue.component('demo-container', demoContainer);
|
||||
|
||||
Vue.prototype.$message = message;
|
||||
@ -132,8 +134,8 @@ const renderTemplate = name => {
|
||||
};
|
||||
|
||||
const demoPaths = fs
|
||||
.readdirSync(path.join(__dirname, `../components/${name}/demo`))
|
||||
.map(p => `../components/${name}/demo/${p}`);
|
||||
.readdirSync(path.join(__dirname, `../antdv-demo/${name}/demo`))
|
||||
.map(p => `../antdv-demo/${name}/demo/${p}`);
|
||||
const testPaths = fs
|
||||
.readdirSync(path.join(__dirname, `../components/test`))
|
||||
.map(p => `../components/test/${p}`);
|
||||
@ -194,7 +196,7 @@ chokidar.watch(configPath, { ignoreInitial: true }).on('change', async () => {
|
||||
|
||||
demoWatcher && (await demoWatcher.close());
|
||||
|
||||
demoWatcher = chokidar.watch(path.join(__dirname, `../components/${componentName}/demo`));
|
||||
demoWatcher = chokidar.watch(path.join(__dirname, `../antdv-demo/${componentName}/demo`));
|
||||
demoWatcher.on('change', () => {
|
||||
renderTemplate(componentName);
|
||||
});
|
||||
|
@ -30,7 +30,7 @@ module.exports = merge(baseWebpackConfig, {
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
port: 3000,
|
||||
port: process.env.PORT || 3000,
|
||||
host: '0.0.0.0',
|
||||
historyApiFallback: {
|
||||
rewrites: [{ from: /./, to: '/index.html' }],
|
||||
|
58
components/__tests__/util/domHook.js
Normal file
58
components/__tests__/util/domHook.js
Normal file
@ -0,0 +1,58 @@
|
||||
const __NULL__ = { notExist: true };
|
||||
|
||||
export function spyElementPrototypes(Element, properties) {
|
||||
const propNames = Object.keys(properties);
|
||||
const originDescriptors = {};
|
||||
|
||||
propNames.forEach(propName => {
|
||||
const originDescriptor = Object.getOwnPropertyDescriptor(Element.prototype, propName);
|
||||
originDescriptors[propName] = originDescriptor || __NULL__;
|
||||
|
||||
const spyProp = properties[propName];
|
||||
|
||||
if (typeof spyProp === 'function') {
|
||||
// If is a function
|
||||
Element.prototype[propName] = function spyFunc(...args) {
|
||||
return spyProp.call(this, originDescriptor, ...args);
|
||||
};
|
||||
} else {
|
||||
// Otherwise tread as a property
|
||||
Object.defineProperty(Element.prototype, propName, {
|
||||
...spyProp,
|
||||
set(value) {
|
||||
if (spyProp.set) {
|
||||
return spyProp.set.call(this, originDescriptor, value);
|
||||
}
|
||||
return originDescriptor.set(value);
|
||||
},
|
||||
get() {
|
||||
if (spyProp.get) {
|
||||
return spyProp.get.call(this, originDescriptor);
|
||||
}
|
||||
return originDescriptor.get();
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
mockRestore() {
|
||||
propNames.forEach(propName => {
|
||||
const originDescriptor = originDescriptors[propName];
|
||||
if (originDescriptor === __NULL__) {
|
||||
delete Element.prototype[propName];
|
||||
} else if (typeof originDescriptor === 'function') {
|
||||
Element.prototype[propName] = originDescriptor;
|
||||
} else {
|
||||
Object.defineProperty(Element.prototype, propName, originDescriptor);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function spyElementPrototype(Element, propName, property) {
|
||||
return spyElementPrototypes(Element, {
|
||||
[propName]: property,
|
||||
});
|
||||
}
|
@ -19,6 +19,7 @@ export default {
|
||||
// Object.assign(newState, this.getDerivedStateFromProps(getOptionProps(this), { ...this.$data, ...newState }, true) || {})
|
||||
// }
|
||||
Object.assign(this.$data, newState);
|
||||
this.$forceUpdate();
|
||||
this.$nextTick(() => {
|
||||
callback && callback();
|
||||
});
|
||||
|
@ -1,61 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
/**
|
||||
* Add class with compatibility for SVG since classList is not supported on
|
||||
* SVG elements in IE
|
||||
*/
|
||||
export function addClass(el, cls) {
|
||||
/* istanbul ignore if */
|
||||
if (!cls || !(cls = cls.trim())) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (el.classList) {
|
||||
if (cls.indexOf(' ') > -1) {
|
||||
cls.split(/\s+/).forEach(c => el.classList.add(c));
|
||||
} else {
|
||||
el.classList.add(cls);
|
||||
}
|
||||
} else {
|
||||
const cur = ` ${el.getAttribute('class') || ''} `;
|
||||
if (cur.indexOf(' ' + cls + ' ') < 0) {
|
||||
el.setAttribute('class', (cur + cls).trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove class with compatibility for SVG since classList is not supported on
|
||||
* SVG elements in IE
|
||||
*/
|
||||
export function removeClass(el, cls) {
|
||||
/* istanbul ignore if */
|
||||
if (!cls || !(cls = cls.trim())) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* istanbul ignore else */
|
||||
if (el.classList) {
|
||||
if (cls.indexOf(' ') > -1) {
|
||||
cls.split(/\s+/).forEach(c => el.classList.remove(c));
|
||||
} else {
|
||||
el.classList.remove(cls);
|
||||
}
|
||||
if (!el.classList.length) {
|
||||
el.removeAttribute('class');
|
||||
}
|
||||
} else {
|
||||
let cur = ` ${el.getAttribute('class') || ''} `;
|
||||
const tar = ' ' + cls + ' ';
|
||||
while (cur.indexOf(tar) >= 0) {
|
||||
cur = cur.replace(tar, ' ');
|
||||
}
|
||||
cur = cur.trim();
|
||||
if (cur) {
|
||||
el.setAttribute('class', cur);
|
||||
} else {
|
||||
el.removeAttribute('class');
|
||||
}
|
||||
}
|
||||
}
|
13
components/_util/__tests__/easings.test.js
Normal file
13
components/_util/__tests__/easings.test.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { easeInOutCubic } from '../easings';
|
||||
|
||||
describe('Test easings', () => {
|
||||
it('easeInOutCubic return value', () => {
|
||||
const nums = [];
|
||||
// eslint-disable-next-line no-plusplus
|
||||
for (let index = 0; index < 5; index++) {
|
||||
nums.push(easeInOutCubic(index, 1, 5, 4));
|
||||
}
|
||||
|
||||
expect(nums).toEqual([1, 1.25, 3, 4.75, 5]);
|
||||
});
|
||||
});
|
56
components/_util/__tests__/scrollTo.test.js
Normal file
56
components/_util/__tests__/scrollTo.test.js
Normal file
@ -0,0 +1,56 @@
|
||||
import scrollTo from '../scrollTo';
|
||||
|
||||
describe('Test ScrollTo function', () => {
|
||||
let dateNowMock;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
dateNowMock = jest
|
||||
.spyOn(Date, 'now')
|
||||
.mockImplementationOnce(() => 0)
|
||||
.mockImplementationOnce(() => 1000);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
dateNowMock.mockRestore();
|
||||
});
|
||||
|
||||
it('test scrollTo', async () => {
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
|
||||
window.scrollY = y;
|
||||
window.pageYOffset = y;
|
||||
});
|
||||
|
||||
scrollTo(1000);
|
||||
|
||||
jest.runAllTimers();
|
||||
expect(window.pageYOffset).toBe(1000);
|
||||
|
||||
scrollToSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('test callback - option', async () => {
|
||||
const cbMock = jest.fn();
|
||||
scrollTo(1000, {
|
||||
callback: cbMock,
|
||||
});
|
||||
jest.runAllTimers();
|
||||
expect(cbMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('test getContainer - option', async () => {
|
||||
const div = document.createElement('div');
|
||||
scrollTo(1000, {
|
||||
getContainer: () => div,
|
||||
});
|
||||
jest.runAllTimers();
|
||||
expect(div.scrollTop).toBe(1000);
|
||||
});
|
||||
});
|
17
components/_util/colors.js
Normal file
17
components/_util/colors.js
Normal file
@ -0,0 +1,17 @@
|
||||
import { tuple } from './type';
|
||||
|
||||
export const PresetColorTypes = tuple(
|
||||
'pink',
|
||||
'red',
|
||||
'yellow',
|
||||
'orange',
|
||||
'cyan',
|
||||
'green',
|
||||
'blue',
|
||||
'purple',
|
||||
'geekblue',
|
||||
'magenta',
|
||||
'volcano',
|
||||
'gold',
|
||||
'lime',
|
||||
);
|
8
components/_util/easings.js
Normal file
8
components/_util/easings.js
Normal file
@ -0,0 +1,8 @@
|
||||
export function easeInOutCubic(t, b, c, d) {
|
||||
const cc = c - b;
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return (cc / 2) * t * t * t + b;
|
||||
}
|
||||
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import animate from './css-animation';
|
||||
const noop = () => {};
|
||||
const getTransitionProps = (transitionName, opt = {}) => {
|
||||
const { beforeEnter, enter, afterEnter, leave, afterLeave, appear = true, tag } = opt;
|
||||
const { beforeEnter, enter, afterEnter, leave, afterLeave, appear = true, tag, nativeOn } = opt;
|
||||
const transitionProps = {
|
||||
props: {
|
||||
appear,
|
||||
@ -22,6 +22,7 @@ const getTransitionProps = (transitionName, opt = {}) => {
|
||||
}),
|
||||
afterLeave: afterLeave || noop,
|
||||
},
|
||||
nativeOn,
|
||||
};
|
||||
// transition-group
|
||||
if (tag) {
|
||||
|
@ -6,7 +6,7 @@ function animate(node, show, done) {
|
||||
let height;
|
||||
let requestAnimationFrameId;
|
||||
let appearRequestAnimationFrameId;
|
||||
return cssAnimation(node, 'ant-motion-collapse', {
|
||||
return cssAnimation(node, 'ant-motion-collapse-legacy', {
|
||||
start() {
|
||||
if (appearRequestAnimationFrameId) {
|
||||
raf.cancel(appearRequestAnimationFrameId);
|
||||
|
@ -259,6 +259,10 @@ export function isEmptyElement(c) {
|
||||
return !(c.tag || (c.text && c.text.trim() !== ''));
|
||||
}
|
||||
|
||||
export function isStringElement(c) {
|
||||
return !c.tag;
|
||||
}
|
||||
|
||||
export function filterEmpty(children = []) {
|
||||
return children.filter(c => !isEmptyElement(c));
|
||||
}
|
||||
|
@ -13,18 +13,20 @@ export default function wrapperRaf(callback, delayFrames = 1) {
|
||||
|
||||
if (restFrames <= 0) {
|
||||
callback();
|
||||
delete ids[id];
|
||||
delete ids[myId];
|
||||
} else {
|
||||
ids[id] = raf(internalCallback);
|
||||
ids[myId] = raf(internalCallback);
|
||||
}
|
||||
}
|
||||
|
||||
ids[id] = raf(internalCallback);
|
||||
ids[myId] = raf(internalCallback);
|
||||
|
||||
return myId;
|
||||
}
|
||||
|
||||
wrapperRaf.cancel = function(pid) {
|
||||
if (pid === undefined) return;
|
||||
raf.cancel(ids[pid]);
|
||||
delete ids[pid];
|
||||
};
|
||||
wrapperRaf.ids = ids; // export this for test usage
|
||||
|
94
components/_util/responsiveObserve.js
Normal file
94
components/_util/responsiveObserve.js
Normal file
@ -0,0 +1,94 @@
|
||||
// matchMedia polyfill for
|
||||
// https://github.com/WickyNilliams/enquire.js/issues/82
|
||||
let enquire;
|
||||
|
||||
// TODO: Will be removed in antd 4.0 because we will no longer support ie9
|
||||
if (typeof window !== 'undefined') {
|
||||
const matchMediaPolyfill = mediaQuery => {
|
||||
return {
|
||||
media: mediaQuery,
|
||||
matches: false,
|
||||
addListener() {},
|
||||
removeListener() {},
|
||||
};
|
||||
};
|
||||
// ref: https://github.com/ant-design/ant-design/issues/18774
|
||||
if (!window.matchMedia) window.matchMedia = matchMediaPolyfill;
|
||||
// eslint-disable-next-line global-require
|
||||
enquire = require('enquire.js');
|
||||
}
|
||||
|
||||
export const responsiveArray = ['xxl', 'xl', 'lg', 'md', 'sm', 'xs'];
|
||||
|
||||
export const responsiveMap = {
|
||||
xs: '(max-width: 575px)',
|
||||
sm: '(min-width: 576px)',
|
||||
md: '(min-width: 768px)',
|
||||
lg: '(min-width: 992px)',
|
||||
xl: '(min-width: 1200px)',
|
||||
xxl: '(min-width: 1600px)',
|
||||
};
|
||||
|
||||
let subscribers = [];
|
||||
let subUid = -1;
|
||||
let screens = {};
|
||||
|
||||
const responsiveObserve = {
|
||||
dispatch(pointMap) {
|
||||
screens = pointMap;
|
||||
if (subscribers.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
subscribers.forEach(item => {
|
||||
item.func(screens);
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
subscribe(func) {
|
||||
if (subscribers.length === 0) {
|
||||
this.register();
|
||||
}
|
||||
const token = (++subUid).toString();
|
||||
subscribers.push({
|
||||
token,
|
||||
func,
|
||||
});
|
||||
func(screens);
|
||||
return token;
|
||||
},
|
||||
unsubscribe(token) {
|
||||
subscribers = subscribers.filter(item => item.token !== token);
|
||||
if (subscribers.length === 0) {
|
||||
this.unregister();
|
||||
}
|
||||
},
|
||||
unregister() {
|
||||
Object.keys(responsiveMap).map(screen => enquire.unregister(responsiveMap[screen]));
|
||||
},
|
||||
register() {
|
||||
Object.keys(responsiveMap).map(screen =>
|
||||
enquire.register(responsiveMap[screen], {
|
||||
match: () => {
|
||||
const pointMap = {
|
||||
...screens,
|
||||
[screen]: true,
|
||||
};
|
||||
this.dispatch(pointMap);
|
||||
},
|
||||
unmatch: () => {
|
||||
const pointMap = {
|
||||
...screens,
|
||||
[screen]: false,
|
||||
};
|
||||
this.dispatch(pointMap);
|
||||
},
|
||||
// Keep a empty destory to avoid triggering unmatch when unregister
|
||||
destroy() {},
|
||||
}),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default responsiveObserve;
|
37
components/_util/scrollTo.js
Normal file
37
components/_util/scrollTo.js
Normal file
@ -0,0 +1,37 @@
|
||||
import raf from 'raf';
|
||||
import getScroll from './getScroll';
|
||||
import { easeInOutCubic } from './easings';
|
||||
|
||||
// interface ScrollToOptions {
|
||||
// /** Scroll container, default as window */
|
||||
// getContainer?: () => HTMLElement | Window;
|
||||
// /** Scroll end callback */
|
||||
// callback?: () => any;
|
||||
// /** Animation duration, default as 450 */
|
||||
// duration?: number;
|
||||
// }
|
||||
|
||||
export default function scrollTo(y, options = {}) {
|
||||
const { getContainer = () => window, callback, duration = 450 } = options;
|
||||
|
||||
const container = getContainer();
|
||||
const scrollTop = getScroll(container, true);
|
||||
const startTime = Date.now();
|
||||
|
||||
const frameFunc = () => {
|
||||
const timestamp = Date.now();
|
||||
const time = timestamp - startTime;
|
||||
const nextScrollTop = easeInOutCubic(time > duration ? duration : time, scrollTop, y, duration);
|
||||
if (container === window) {
|
||||
window.scrollTo(window.pageXOffset, nextScrollTop);
|
||||
} else {
|
||||
container.scrollTop = nextScrollTop;
|
||||
}
|
||||
if (time < duration) {
|
||||
raf(frameFunc);
|
||||
} else if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
raf(frameFunc);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
function isStyleSupport(styleName) {
|
||||
const isStyleSupport = styleName => {
|
||||
if (typeof window !== 'undefined' && window.document && window.document.documentElement) {
|
||||
const styleNameList = Array.isArray(styleName) ? styleName : [styleName];
|
||||
const { documentElement } = window.document;
|
||||
@ -6,7 +6,7 @@ function isStyleSupport(styleName) {
|
||||
return styleNameList.some(name => name in documentElement.style);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const isFlexSupported = isStyleSupport(['flex', 'webkitFlex', 'Flex', 'msFlex']);
|
||||
|
||||
|
81
components/_util/transButton.jsx
Normal file
81
components/_util/transButton.jsx
Normal file
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Wrap of sub component which need use as Button capacity (like Icon component).
|
||||
* This helps accessibility reader to tread as a interactive button to operation.
|
||||
*/
|
||||
import KeyCode from './KeyCode';
|
||||
import PropTypes from './vue-types';
|
||||
|
||||
const inlineStyle = {
|
||||
border: 0,
|
||||
background: 'transparent',
|
||||
padding: 0,
|
||||
lineHeight: 'inherit',
|
||||
display: 'inline-block',
|
||||
};
|
||||
|
||||
const TransButton = {
|
||||
props: {
|
||||
noStyle: PropTypes.bool,
|
||||
},
|
||||
|
||||
methods: {
|
||||
onKeyDown(event) {
|
||||
const { keyCode } = event;
|
||||
if (keyCode === KeyCode.ENTER) {
|
||||
event.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
onKeyUp(event) {
|
||||
const { keyCode } = event;
|
||||
if (keyCode === KeyCode.ENTER) {
|
||||
this.$emit('click', event);
|
||||
}
|
||||
},
|
||||
|
||||
setRef(btn) {
|
||||
this.div = btn;
|
||||
},
|
||||
|
||||
focus() {
|
||||
if (this.div) {
|
||||
this.div.focus();
|
||||
}
|
||||
},
|
||||
|
||||
blur() {
|
||||
if (this.div) {
|
||||
this.div.blur();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
const { noStyle } = this.$props;
|
||||
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
{...{
|
||||
directives: [
|
||||
{
|
||||
name: 'ant-ref',
|
||||
value: this.setRef,
|
||||
},
|
||||
],
|
||||
on: {
|
||||
...this.$listeners,
|
||||
keydown: this.onKeyDown,
|
||||
keyup: this.onKeyUp,
|
||||
},
|
||||
}}
|
||||
style={{ ...(!noStyle ? inlineStyle : null) }}
|
||||
>
|
||||
{this.$slots.default}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export default TransButton;
|
4
components/_util/type.js
Normal file
4
components/_util/type.js
Normal file
@ -0,0 +1,4 @@
|
||||
// https://stackoverflow.com/questions/46176165/ways-to-get-string-literal-type-of-array-values-without-enum-overhead
|
||||
export const tuple = (...args) => args;
|
||||
|
||||
export const tupleNum = (...args) => args;
|
@ -1,38 +1,7 @@
|
||||
/* eslint-disable no-console */
|
||||
let warned = {};
|
||||
import warning, { resetWarned } from '../vc-util/warning';
|
||||
|
||||
export function warning(valid, message) {
|
||||
// Support uglify
|
||||
if (process.env.NODE_ENV !== 'production' && !valid && console !== undefined) {
|
||||
console.error(`Warning: ${message}`);
|
||||
}
|
||||
}
|
||||
export { resetWarned };
|
||||
|
||||
export function note(valid, message) {
|
||||
// Support uglify
|
||||
if (process.env.NODE_ENV !== 'production' && !valid && console !== undefined) {
|
||||
console.warn(`Note: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function resetWarned() {
|
||||
warned = {};
|
||||
}
|
||||
|
||||
export function call(method, valid, message) {
|
||||
if (!valid && !warned[message]) {
|
||||
method(false, message);
|
||||
warned[message] = true;
|
||||
}
|
||||
}
|
||||
|
||||
export function warningOnce(valid, message) {
|
||||
call(warning, valid, message);
|
||||
}
|
||||
|
||||
export function noteOnce(valid, message) {
|
||||
call(note, valid, message);
|
||||
}
|
||||
|
||||
export default warningOnce;
|
||||
/* eslint-enable */
|
||||
export default (valid, component, message = '') => {
|
||||
warning(valid, `[antdv: ${component}] ${message}`);
|
||||
};
|
||||
|
@ -1,5 +1,6 @@
|
||||
import TransitionEvents from './css-animation/Event';
|
||||
import raf from '../_util/raf';
|
||||
import raf from './raf';
|
||||
import { ConfigConsumerProps } from '../config-provider';
|
||||
let styleForPesudo;
|
||||
|
||||
// Where el is the DOM element you'd like to test for visibility
|
||||
@ -9,7 +10,14 @@ function isHidden(element) {
|
||||
}
|
||||
return !element || element.offsetParent === null;
|
||||
}
|
||||
|
||||
function isNotGrey(color) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/);
|
||||
if (match && match[1] && match[2] && match[3]) {
|
||||
return !(match[1] === match[2] && match[2] === match[3]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
export default {
|
||||
name: 'Wave',
|
||||
props: ['insertExtraNode'],
|
||||
@ -22,7 +30,9 @@ export default {
|
||||
this.instance = this.bindAnimationEvent(node);
|
||||
});
|
||||
},
|
||||
|
||||
inject: {
|
||||
configProvider: { default: () => ConfigConsumerProps },
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.instance) {
|
||||
this.instance.cancel();
|
||||
@ -33,19 +43,10 @@ export default {
|
||||
this.destroy = true;
|
||||
},
|
||||
methods: {
|
||||
isNotGrey(color) {
|
||||
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/);
|
||||
if (match && match[1] && match[2] && match[3]) {
|
||||
return !(match[1] === match[2] && match[2] === match[3]);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
onClick(node, waveColor) {
|
||||
if (!node || isHidden(node) || node.className.indexOf('-leave') >= 0) {
|
||||
return;
|
||||
}
|
||||
this.removeExtraStyleNode();
|
||||
const { insertExtraNode } = this.$props;
|
||||
this.extraNode = document.createElement('div');
|
||||
const extraNode = this.extraNode;
|
||||
@ -59,13 +60,19 @@ export default {
|
||||
waveColor &&
|
||||
waveColor !== '#ffffff' &&
|
||||
waveColor !== 'rgb(255, 255, 255)' &&
|
||||
this.isNotGrey(waveColor) &&
|
||||
isNotGrey(waveColor) &&
|
||||
!/rgba\(\d*, \d*, \d*, 0\)/.test(waveColor) && // any transparent rgba color
|
||||
waveColor !== 'transparent'
|
||||
) {
|
||||
// Add nonce if CSP exist
|
||||
if (this.csp && this.csp.nonce) {
|
||||
styleForPesudo.nonce = this.csp.nonce;
|
||||
}
|
||||
extraNode.style.borderColor = waveColor;
|
||||
|
||||
styleForPesudo.innerHTML = `[ant-click-animating-without-extra-node]:after { border-color: ${waveColor}; }`;
|
||||
styleForPesudo.innerHTML = `
|
||||
[ant-click-animating-without-extra-node='true']::after, .ant-click-animating-node {
|
||||
--antd-wave-shadow-color: ${waveColor};
|
||||
}`;
|
||||
if (!document.body.contains(styleForPesudo)) {
|
||||
document.body.appendChild(styleForPesudo);
|
||||
}
|
||||
@ -76,7 +83,28 @@ export default {
|
||||
TransitionEvents.addStartEventListener(node, this.onTransitionStart);
|
||||
TransitionEvents.addEndEventListener(node, this.onTransitionEnd);
|
||||
},
|
||||
onTransitionStart(e) {
|
||||
if (this.destroy) return;
|
||||
|
||||
const node = this.$el;
|
||||
if (!e || e.target !== node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.animationStart) {
|
||||
this.resetEffect(node);
|
||||
}
|
||||
},
|
||||
onTransitionEnd(e) {
|
||||
if (!e || e.animationName !== 'fadeEffect') {
|
||||
return;
|
||||
}
|
||||
this.resetEffect(e.target);
|
||||
},
|
||||
getAttributeName() {
|
||||
const { insertExtraNode } = this.$props;
|
||||
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
|
||||
},
|
||||
bindAnimationEvent(node) {
|
||||
if (
|
||||
!node ||
|
||||
@ -113,10 +141,6 @@ export default {
|
||||
},
|
||||
};
|
||||
},
|
||||
getAttributeName() {
|
||||
const { insertExtraNode } = this.$props;
|
||||
return insertExtraNode ? 'ant-click-animating' : 'ant-click-animating-without-extra-node';
|
||||
},
|
||||
|
||||
resetEffect(node) {
|
||||
if (!node || node === this.extraNode || !(node instanceof Element)) {
|
||||
@ -124,40 +148,22 @@ export default {
|
||||
}
|
||||
const { insertExtraNode } = this.$props;
|
||||
const attributeName = this.getAttributeName();
|
||||
node.removeAttribute(attributeName);
|
||||
this.removeExtraStyleNode();
|
||||
node.setAttribute(attributeName, 'false'); // edge has bug on `removeAttribute` #14466
|
||||
if (styleForPesudo) {
|
||||
styleForPesudo.innerHTML = '';
|
||||
}
|
||||
if (insertExtraNode && this.extraNode && node.contains(this.extraNode)) {
|
||||
node.removeChild(this.extraNode);
|
||||
}
|
||||
TransitionEvents.removeStartEventListener(node, this.onTransitionStart);
|
||||
TransitionEvents.removeEndEventListener(node, this.onTransitionEnd);
|
||||
},
|
||||
onTransitionStart(e) {
|
||||
if (this.destroy) return;
|
||||
|
||||
const node = this.$el;
|
||||
if (!e || e.target !== node) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.animationStart) {
|
||||
this.resetEffect(node);
|
||||
}
|
||||
},
|
||||
onTransitionEnd(e) {
|
||||
if (!e || e.animationName !== 'fadeEffect') {
|
||||
return;
|
||||
}
|
||||
this.resetEffect(e.target);
|
||||
},
|
||||
removeExtraStyleNode() {
|
||||
if (styleForPesudo) {
|
||||
styleForPesudo.innerHTML = '';
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.configProvider.csp) {
|
||||
this.csp = this.configProvider.csp;
|
||||
}
|
||||
return this.$slots.default && this.$slots.default[0];
|
||||
},
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/affix/demo/basic.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/affix/demo/basic.md correctly 1`] = `
|
||||
<div>
|
||||
<div>
|
||||
<div class=""><button type="button" class="ant-btn ant-btn-primary"><span>Affix top</span></button></div>
|
||||
@ -11,13 +11,13 @@ exports[`renders ./components/affix/demo/basic.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/affix/demo/on-change.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/affix/demo/on-change.md correctly 1`] = `
|
||||
<div>
|
||||
<div class=""><button type="button" class="ant-btn"><span>120px to affix top</span></button></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/affix/demo/target.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/affix/demo/target.md correctly 1`] = `
|
||||
<div id="components-affix-demo-target" class="scrollable-container">
|
||||
<div class="background">
|
||||
<div>
|
||||
|
@ -1,6 +1,8 @@
|
||||
import Affix from '..';
|
||||
import Button from '../../button';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { spyElementPrototype } from '../../__tests__/util/domHook';
|
||||
import { asyncExpect } from '@/tests/utils';
|
||||
const events = {};
|
||||
|
||||
const AffixMounter = {
|
||||
@ -18,21 +20,14 @@ const AffixMounter = {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: '100px',
|
||||
overflowY: 'scroll',
|
||||
}}
|
||||
ref="container"
|
||||
>
|
||||
<div
|
||||
className="background"
|
||||
style={{
|
||||
paddingTop: '60px',
|
||||
height: '300px',
|
||||
}}
|
||||
>
|
||||
<Affix target={() => this.$refs.container} ref="affix" {...{ props: this.$props }}>
|
||||
<div>
|
||||
<div ref="container" class="container">
|
||||
<Affix
|
||||
class="fixed"
|
||||
target={() => this.$refs.container}
|
||||
ref="affix"
|
||||
{...{ props: this.$props }}
|
||||
>
|
||||
<Button type="primary">Fixed at the top of container</Button>
|
||||
</Affix>
|
||||
</div>
|
||||
@ -42,26 +37,34 @@ const AffixMounter = {
|
||||
};
|
||||
describe('Affix Render', () => {
|
||||
let wrapper;
|
||||
let domMock;
|
||||
const classRect = {
|
||||
container: {
|
||||
top: 1000,
|
||||
bottom: 100,
|
||||
},
|
||||
};
|
||||
beforeAll(() => {
|
||||
document.body.innerHTML = '';
|
||||
jest.useFakeTimers();
|
||||
domMock = spyElementPrototype(HTMLElement, 'getBoundingClientRect', function mockBounding() {
|
||||
return (
|
||||
classRect[this.className] || {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
domMock.mockRestore();
|
||||
});
|
||||
const scrollTo = top => {
|
||||
wrapper.vm.$refs.affix.$refs.fixedNode.parentNode.getBoundingClientRect = jest.fn(() => {
|
||||
return {
|
||||
bottom: 100,
|
||||
height: 28,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 50 - top,
|
||||
width: 195,
|
||||
};
|
||||
});
|
||||
wrapper.vm.$refs.container.scrollTop = top;
|
||||
const movePlaceholder = top => {
|
||||
classRect.fixed = {
|
||||
top: top,
|
||||
bottom: top,
|
||||
};
|
||||
events.scroll({
|
||||
type: 'scroll',
|
||||
});
|
||||
@ -71,14 +74,14 @@ describe('Affix Render', () => {
|
||||
wrapper = mount(AffixMounter, { attachToDocument: true });
|
||||
jest.runAllTimers();
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.vm.$refs.affix.affixStyle).toBe(null);
|
||||
movePlaceholder(0);
|
||||
expect(wrapper.vm.$refs.affix.affixStyle).toBeFalsy();
|
||||
|
||||
scrollTo(100);
|
||||
expect(wrapper.vm.$refs.affix.affixStyle).not.toBe(null);
|
||||
// movePlaceholder(100);
|
||||
// expect(wrapper.vm.$refs.affix.affixStyle).toBeTruthy();
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.vm.$refs.affix.affixStyle).toBe(null);
|
||||
movePlaceholder(0);
|
||||
expect(wrapper.vm.$refs.affix.affixStyle).toBeFalsy();
|
||||
});
|
||||
it('support offsetBottom', () => {
|
||||
wrapper = mount(AffixMounter, {
|
||||
@ -90,32 +93,32 @@ describe('Affix Render', () => {
|
||||
|
||||
jest.runAllTimers();
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.vm.$refs.affix.affixStyle).not.toBe(null);
|
||||
movePlaceholder(300);
|
||||
//expect(wrapper.vm.$refs.affix.affixStyle).toBeTruthy();
|
||||
|
||||
scrollTo(100);
|
||||
expect(wrapper.vm.$refs.affix.affixStyle).toBe(null);
|
||||
movePlaceholder(0);
|
||||
expect(wrapper.vm.$refs.affix.affixStyle).toBeFalsy();
|
||||
|
||||
scrollTo(0);
|
||||
expect(wrapper.vm.$refs.affix.affixStyle).not.toBe(null);
|
||||
// movePlaceholder(300);
|
||||
// expect(wrapper.vm.$refs.affix.affixStyle).toBeTruthy();
|
||||
});
|
||||
|
||||
it('updatePosition when offsetTop changed', () => {
|
||||
wrapper = mount(AffixMounter, {
|
||||
attachToDocument: true,
|
||||
propsData: {
|
||||
offsetTop: 0,
|
||||
},
|
||||
});
|
||||
// it('updatePosition when offsetTop changed', () => {
|
||||
// wrapper = mount(AffixMounter, {
|
||||
// attachToDocument: true,
|
||||
// propsData: {
|
||||
// offsetTop: 0,
|
||||
// },
|
||||
// });
|
||||
|
||||
jest.runAllTimers();
|
||||
// jest.runAllTimers();
|
||||
|
||||
scrollTo(100);
|
||||
expect(wrapper.vm.$refs.affix.affixStyle.top).toBe('0px');
|
||||
wrapper.setProps({
|
||||
offsetTop: 10,
|
||||
});
|
||||
jest.runAllTimers();
|
||||
expect(wrapper.vm.$refs.affix.affixStyle.top).toBe('10px');
|
||||
});
|
||||
// movePlaceholder(-100);
|
||||
// expect(wrapper.vm.$refs.affix.affixStyle.top).toBe('0px');
|
||||
// wrapper.setProps({
|
||||
// offsetTop: 10,
|
||||
// });
|
||||
// jest.runAllTimers();
|
||||
// expect(wrapper.vm.$refs.affix.affixStyle.top).toBe('10px');
|
||||
// });
|
||||
});
|
||||
|
@ -1,34 +0,0 @@
|
||||
<cn>
|
||||
#### 基本
|
||||
最简单的用法。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### basic
|
||||
The simplest usage.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-affix :offsetTop="this.top">
|
||||
<a-button type="primary" @click="()=>{this.top += 10}">Affix top</a-button>
|
||||
</a-affix>
|
||||
<br />
|
||||
<a-affix :offsetBottom="this.bottom">
|
||||
<a-button type="primary" @click="()=>{this.bottom += 10}">Affix bottom</a-button>
|
||||
</a-affix>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
@ -1,42 +0,0 @@
|
||||
<script>
|
||||
import Basic from './basic';
|
||||
import Onchange from './on-change';
|
||||
import Target from './target';
|
||||
import CN from '../index.zh-CN.md';
|
||||
import US from '../index.en-US.md';
|
||||
const md = {
|
||||
cn: `# Affix 固钉
|
||||
将页面元素钉在可视范围。
|
||||
## 何时使用
|
||||
当内容区域比较长,需要滚动页面时,这部分内容对应的操作或者导航需要在滚动范围内始终展现。常用于侧边菜单和按钮组合。
|
||||
页面可视范围过小时,慎用此功能以免遮挡页面内容。
|
||||
## 代码演示`,
|
||||
us: `# Affix
|
||||
Make an element stick to viewport.
|
||||
## When To Use
|
||||
When user browses a long web page, some content need to stick to the viewport. This is common for menus and actions.
|
||||
Please note that Affix should not cover other content on the page, especially when the size of the viewport is small.
|
||||
## Examples `,
|
||||
};
|
||||
export default {
|
||||
category: 'Components',
|
||||
subtitle: '固钉',
|
||||
zhType: '导航',
|
||||
type: 'Navigation',
|
||||
title: 'Affix',
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<md cn={md.cn} us={md.us} />
|
||||
<Basic />
|
||||
<Onchange />
|
||||
<Target />
|
||||
<api>
|
||||
<CN slot="cn" />
|
||||
<US />
|
||||
</api>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
</script>
|
@ -1,26 +0,0 @@
|
||||
<cn>
|
||||
#### 固定状态改变的回调
|
||||
可以获得是否固定的状态。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Callback
|
||||
Callback with affixed state.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<a-affix :offsetTop="120" @change="change">
|
||||
<a-button>120px to affix top</a-button>
|
||||
</a-affix>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
change(affixed) {
|
||||
console.log(affixed);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
@ -1,34 +0,0 @@
|
||||
<cn>
|
||||
#### 滚动容器
|
||||
用 `target` 设置 `Affix` 需要监听其滚动事件的元素,默认为 `window`。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Container to scroll.
|
||||
Set a `target` for 'Affix', which is listen to scroll event of target element (default is `window`).
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div id="components-affix-demo-target" class="scrollable-container" ref="container">
|
||||
<div class="background">
|
||||
<a-affix :target="() => this.$refs.container">
|
||||
<a-button type="primary">
|
||||
Fixed at the top of container
|
||||
</a-button>
|
||||
</a-affix>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
#components-affix-demo-target.scrollable-container {
|
||||
height: 100px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
#components-affix-demo-target .background {
|
||||
padding-top: 60px;
|
||||
height: 300px;
|
||||
background-image: url('https://zos.alipayobjects.com/rmsportal/RmjwQiJorKyobvI.jpg');
|
||||
}
|
||||
</style>
|
||||
```
|
@ -1,21 +0,0 @@
|
||||
## API
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| offsetBottom | Pixels to offset from bottom when calculating position of scroll | number | - |
|
||||
| offsetTop | Pixels to offset from top when calculating position of scroll | number | 0 |
|
||||
| target | specifies the scrollable area dom node | () => HTMLElement | () => window |
|
||||
|
||||
### events
|
||||
|
||||
| Events Name | Description | Arguments |
|
||||
| ----------- | ---------------------------------------- | ----------------- |
|
||||
| onChange | Callback for when affix state is changed | Function(affixed) |
|
||||
|
||||
**Note:** Children of `Affix` can not be `position: absolute`, but you can set `Affix` as `position: absolute`:
|
||||
|
||||
```html
|
||||
<a-affix :style="{ position: 'absolute', top: y, left: x}">
|
||||
...
|
||||
</a-affix>
|
||||
```
|
@ -1,36 +1,20 @@
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import addEventListener from '../_util/Dom/addEventListener';
|
||||
import classNames from 'classnames';
|
||||
import shallowequal from 'shallowequal';
|
||||
import omit from 'omit.js';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import ResizeObserver from '../vc-resize-observer';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame';
|
||||
import { ConfigConsumerProps } from '../config-provider';
|
||||
import Base from '../base';
|
||||
|
||||
function getTargetRect(target) {
|
||||
return target !== window ? target.getBoundingClientRect() : { top: 0, left: 0, bottom: 0 };
|
||||
}
|
||||
|
||||
function getOffset(element, target) {
|
||||
const elemRect = element.getBoundingClientRect();
|
||||
const targetRect = getTargetRect(target);
|
||||
|
||||
const scrollTop = getScroll(target, true);
|
||||
const scrollLeft = getScroll(target, false);
|
||||
|
||||
const docElem = window.document.body;
|
||||
const clientTop = docElem.clientTop || 0;
|
||||
const clientLeft = docElem.clientLeft || 0;
|
||||
|
||||
return {
|
||||
top: elemRect.top - targetRect.top + scrollTop - clientTop,
|
||||
left: elemRect.left - targetRect.left + scrollLeft - clientLeft,
|
||||
width: elemRect.width,
|
||||
height: elemRect.height,
|
||||
};
|
||||
}
|
||||
import warning from '../_util/warning';
|
||||
import {
|
||||
addObserveTarget,
|
||||
removeObserveTarget,
|
||||
getTargetRect,
|
||||
getFixedTop,
|
||||
getFixedBottom,
|
||||
} from './utils';
|
||||
|
||||
function getDefaultTarget() {
|
||||
return typeof window !== 'undefined' ? window : null;
|
||||
@ -48,10 +32,13 @@ const AffixProps = {
|
||||
/** 固定状态改变时触发的回调函数 */
|
||||
// onChange?: (affixed?: boolean) => void;
|
||||
/** 设置 Affix 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 */
|
||||
target: PropTypes.func,
|
||||
target: PropTypes.func.def(getDefaultTarget),
|
||||
prefixCls: PropTypes.string,
|
||||
};
|
||||
|
||||
const AffixStatus = {
|
||||
None: 'none',
|
||||
Prepare: 'Prepare',
|
||||
};
|
||||
const Affix = {
|
||||
name: 'AAffix',
|
||||
props: AffixProps,
|
||||
@ -60,192 +47,191 @@ const Affix = {
|
||||
configProvider: { default: () => ConfigConsumerProps },
|
||||
},
|
||||
data() {
|
||||
this.events = ['resize', 'scroll', 'touchstart', 'touchmove', 'touchend', 'pageshow', 'load'];
|
||||
this.eventHandlers = {};
|
||||
return {
|
||||
affixStyle: undefined,
|
||||
placeholderStyle: undefined,
|
||||
status: AffixStatus.None,
|
||||
lastAffix: false,
|
||||
prevTarget: null,
|
||||
};
|
||||
},
|
||||
beforeMount() {
|
||||
this.updatePosition = throttleByAnimationFrame(this.updatePosition);
|
||||
this.lazyUpdatePosition = throttleByAnimationFrame(this.lazyUpdatePosition);
|
||||
},
|
||||
mounted() {
|
||||
const target = this.target || getDefaultTarget;
|
||||
// Wait for parent component ref has its value
|
||||
this.timeout = setTimeout(() => {
|
||||
this.setTargetEventListeners(target);
|
||||
// Mock Event object.
|
||||
this.updatePosition({});
|
||||
});
|
||||
const { target } = this;
|
||||
if (target) {
|
||||
// [Legacy] Wait for parent component ref has its value.
|
||||
// We should use target as directly element instead of function which makes element check hard.
|
||||
this.timeout = setTimeout(() => {
|
||||
addObserveTarget(target(), this);
|
||||
// Mock Event object.
|
||||
this.updatePosition();
|
||||
});
|
||||
}
|
||||
},
|
||||
updated() {
|
||||
this.measure();
|
||||
},
|
||||
watch: {
|
||||
target(val) {
|
||||
this.clearEventListeners();
|
||||
this.setTargetEventListeners(val);
|
||||
// Mock Event object.
|
||||
this.updatePosition({});
|
||||
let newTarget = null;
|
||||
if (val) {
|
||||
newTarget = val() || null;
|
||||
}
|
||||
if (this.prevTarget !== newTarget) {
|
||||
removeObserveTarget(this);
|
||||
if (newTarget) {
|
||||
addObserveTarget(newTarget, this);
|
||||
// Mock Event object.
|
||||
this.updatePosition();
|
||||
}
|
||||
this.prevTarget = newTarget;
|
||||
}
|
||||
},
|
||||
offsetTop() {
|
||||
this.updatePosition({});
|
||||
this.updatePosition();
|
||||
},
|
||||
offsetBottom() {
|
||||
this.updatePosition({});
|
||||
this.updatePosition();
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.clearEventListeners();
|
||||
clearTimeout(this.timeout);
|
||||
removeObserveTarget(this);
|
||||
this.updatePosition.cancel();
|
||||
},
|
||||
methods: {
|
||||
setAffixStyle(e, affixStyle) {
|
||||
const { target = getDefaultTarget } = this;
|
||||
const originalAffixStyle = this.affixStyle;
|
||||
const isWindow = target() === window;
|
||||
if (e.type === 'scroll' && originalAffixStyle && affixStyle && isWindow) {
|
||||
return;
|
||||
}
|
||||
if (shallowequal(affixStyle, originalAffixStyle)) {
|
||||
return;
|
||||
}
|
||||
this.setState({ affixStyle: affixStyle }, () => {
|
||||
const affixed = !!this.affixStyle;
|
||||
if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) {
|
||||
this.$emit('change', affixed);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setPlaceholderStyle(placeholderStyle) {
|
||||
const originalPlaceholderStyle = this.placeholderStyle;
|
||||
if (shallowequal(placeholderStyle, originalPlaceholderStyle)) {
|
||||
return;
|
||||
}
|
||||
this.setState({ placeholderStyle: placeholderStyle });
|
||||
},
|
||||
syncPlaceholderStyle(e) {
|
||||
const { affixStyle } = this;
|
||||
if (!affixStyle) {
|
||||
return;
|
||||
}
|
||||
this.$refs.placeholderNode.style.cssText = '';
|
||||
this.setAffixStyle(e, {
|
||||
...affixStyle,
|
||||
width: this.$refs.placeholderNode.offsetWidth + 'px',
|
||||
});
|
||||
this.setPlaceholderStyle({
|
||||
width: this.$refs.placeholderNode.offsetWidth + 'px',
|
||||
});
|
||||
},
|
||||
|
||||
updatePosition(e) {
|
||||
const { offsetBottom, offset, target = getDefaultTarget } = this;
|
||||
getOffsetTop() {
|
||||
const { offset, offsetBottom } = this;
|
||||
let { offsetTop } = this;
|
||||
const targetNode = target();
|
||||
if (typeof offsetTop === 'undefined') {
|
||||
offsetTop = offset;
|
||||
warning(
|
||||
typeof offset === 'undefined',
|
||||
'Affix',
|
||||
'`offset` is deprecated. Please use `offsetTop` instead.',
|
||||
);
|
||||
}
|
||||
|
||||
// Backwards support
|
||||
// Fix: if offsetTop === 0, it will get undefined,
|
||||
// if offsetBottom is type of number, offsetMode will be { top: false, ... }
|
||||
offsetTop = typeof offsetTop === 'undefined' ? offset : offsetTop;
|
||||
const scrollTop = getScroll(targetNode, true);
|
||||
const affixNode = this.$el;
|
||||
const elemOffset = getOffset(affixNode, targetNode);
|
||||
const elemSize = {
|
||||
width: this.$refs.fixedNode.offsetWidth,
|
||||
height: this.$refs.fixedNode.offsetHeight,
|
||||
};
|
||||
|
||||
const offsetMode = {
|
||||
top: false,
|
||||
bottom: false,
|
||||
};
|
||||
// Default to `offsetTop=0`.
|
||||
if (typeof offsetTop !== 'number' && typeof offsetBottom !== 'number') {
|
||||
offsetMode.top = true;
|
||||
if (offsetBottom === undefined && offsetTop === undefined) {
|
||||
offsetTop = 0;
|
||||
} else {
|
||||
offsetMode.top = typeof offsetTop === 'number';
|
||||
offsetMode.bottom = typeof offsetBottom === 'number';
|
||||
}
|
||||
|
||||
const targetRect = getTargetRect(targetNode);
|
||||
const targetInnerHeight = targetNode.innerHeight || targetNode.clientHeight;
|
||||
// ref: https://github.com/ant-design/ant-design/issues/13662
|
||||
if (scrollTop >= elemOffset.top - offsetTop && offsetMode.top) {
|
||||
// Fixed Top
|
||||
const width = `${elemOffset.width}px`;
|
||||
const top = `${targetRect.top + offsetTop}px`;
|
||||
this.setAffixStyle(e, {
|
||||
position: 'fixed',
|
||||
top,
|
||||
left: `${targetRect.left + elemOffset.left}px`,
|
||||
width,
|
||||
});
|
||||
this.setPlaceholderStyle({
|
||||
width,
|
||||
height: `${elemSize.height}px`,
|
||||
});
|
||||
} else if (
|
||||
scrollTop <= elemOffset.top + elemSize.height + offsetBottom - targetInnerHeight &&
|
||||
offsetMode.bottom
|
||||
) {
|
||||
// Fixed Bottom
|
||||
const targetBottomOffet =
|
||||
targetNode === window ? 0 : window.innerHeight - targetRect.bottom;
|
||||
const width = `${elemOffset.width}px`;
|
||||
this.setAffixStyle(e, {
|
||||
position: 'fixed',
|
||||
bottom: targetBottomOffet + offsetBottom + 'px',
|
||||
left: targetRect.left + elemOffset.left + 'px',
|
||||
width,
|
||||
});
|
||||
this.setPlaceholderStyle({
|
||||
width,
|
||||
height: elemOffset.height + 'px',
|
||||
});
|
||||
} else {
|
||||
const { affixStyle } = this;
|
||||
if (
|
||||
e.type === 'resize' &&
|
||||
affixStyle &&
|
||||
affixStyle.position === 'fixed' &&
|
||||
affixNode.offsetWidth
|
||||
) {
|
||||
this.setAffixStyle(e, { ...affixStyle, width: affixNode.offsetWidth + 'px' });
|
||||
} else {
|
||||
this.setAffixStyle(e, null);
|
||||
}
|
||||
this.setPlaceholderStyle(null);
|
||||
}
|
||||
if (e.type === 'resize') {
|
||||
this.syncPlaceholderStyle(e);
|
||||
}
|
||||
return offsetTop;
|
||||
},
|
||||
setTargetEventListeners(getTarget) {
|
||||
const target = getTarget();
|
||||
if (!target) {
|
||||
|
||||
getOffsetBottom() {
|
||||
return this.offsetBottom;
|
||||
},
|
||||
// =================== Measure ===================
|
||||
measure() {
|
||||
const { status, lastAffix } = this;
|
||||
const { target } = this;
|
||||
if (
|
||||
status !== AffixStatus.Prepare ||
|
||||
!this.$refs.fixedNode ||
|
||||
!this.$refs.placeholderNode ||
|
||||
!target
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.clearEventListeners();
|
||||
|
||||
this.events.forEach(eventName => {
|
||||
this.eventHandlers[eventName] = addEventListener(target, eventName, this.updatePosition);
|
||||
});
|
||||
const offsetTop = this.getOffsetTop();
|
||||
const offsetBottom = this.getOffsetBottom();
|
||||
|
||||
const targetNode = target();
|
||||
if (!targetNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newState = {
|
||||
status: AffixStatus.None,
|
||||
};
|
||||
const targetRect = getTargetRect(targetNode);
|
||||
const placeholderReact = getTargetRect(this.$refs.placeholderNode);
|
||||
const fixedTop = getFixedTop(placeholderReact, targetRect, offsetTop);
|
||||
const fixedBottom = getFixedBottom(placeholderReact, targetRect, offsetBottom);
|
||||
if (fixedTop !== undefined) {
|
||||
newState.affixStyle = {
|
||||
position: 'fixed',
|
||||
top: fixedTop,
|
||||
width: placeholderReact.width + 'px',
|
||||
height: placeholderReact.height + 'px',
|
||||
};
|
||||
newState.placeholderStyle = {
|
||||
width: placeholderReact.width + 'px',
|
||||
height: placeholderReact.height + 'px',
|
||||
};
|
||||
} else if (fixedBottom !== undefined) {
|
||||
newState.affixStyle = {
|
||||
position: 'fixed',
|
||||
bottom: fixedBottom,
|
||||
width: placeholderReact.width + 'px',
|
||||
height: placeholderReact.height + 'px',
|
||||
};
|
||||
newState.placeholderStyle = {
|
||||
width: placeholderReact.width + 'px',
|
||||
height: placeholderReact.height + 'px',
|
||||
};
|
||||
}
|
||||
|
||||
newState.lastAffix = !!newState.affixStyle;
|
||||
if (lastAffix !== newState.lastAffix) {
|
||||
this.$emit('change', newState.lastAffix);
|
||||
}
|
||||
|
||||
this.setState(newState);
|
||||
},
|
||||
|
||||
clearEventListeners() {
|
||||
this.events.forEach(eventName => {
|
||||
const handler = this.eventHandlers[eventName];
|
||||
if (handler && handler.remove) {
|
||||
handler.remove();
|
||||
}
|
||||
// @ts-ignore TS6133
|
||||
prepareMeasure() {
|
||||
this.setState({
|
||||
status: AffixStatus.Prepare,
|
||||
affixStyle: undefined,
|
||||
placeholderStyle: undefined,
|
||||
});
|
||||
this.$forceUpdate();
|
||||
|
||||
// Test if `updatePosition` called
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
this.$emit('testUpdatePosition');
|
||||
}
|
||||
},
|
||||
updatePosition() {
|
||||
this.prepareMeasure();
|
||||
},
|
||||
lazyUpdatePosition() {
|
||||
const { target } = this;
|
||||
const { affixStyle } = this;
|
||||
|
||||
// Check position change before measure to make Safari smooth
|
||||
if (target && affixStyle) {
|
||||
const offsetTop = this.getOffsetTop();
|
||||
const offsetBottom = this.getOffsetBottom();
|
||||
|
||||
const targetNode = target();
|
||||
if (targetNode && this.$refs.placeholderNode) {
|
||||
const targetRect = getTargetRect(targetNode);
|
||||
const placeholderReact = getTargetRect(this.$refs.placeholderNode);
|
||||
const fixedTop = getFixedTop(placeholderReact, targetRect, offsetTop);
|
||||
const fixedBottom = getFixedBottom(placeholderReact, targetRect, offsetBottom);
|
||||
|
||||
if (
|
||||
(fixedTop !== undefined && affixStyle.top === fixedTop) ||
|
||||
(fixedBottom !== undefined && affixStyle.bottom === fixedBottom)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Directly call prepare measure since it's already throttled.
|
||||
this.prepareMeasure();
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
const { prefixCls, affixStyle, placeholderStyle, $slots, $props } = this;
|
||||
const { prefixCls, affixStyle, placeholderStyle, status, $slots, $props } = this;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const className = classNames({
|
||||
[getPrefixCls('affix', prefixCls)]: affixStyle,
|
||||
@ -255,11 +241,17 @@ const Affix = {
|
||||
attrs: omit($props, ['prefixCls', 'offsetTop', 'offsetBottom', 'target']),
|
||||
};
|
||||
return (
|
||||
<div {...props} style={placeholderStyle} ref="placeholderNode">
|
||||
<div class={className} ref="fixedNode" style={affixStyle}>
|
||||
{$slots.default}
|
||||
<ResizeObserver
|
||||
onResize={() => {
|
||||
this.updatePosition();
|
||||
}}
|
||||
>
|
||||
<div {...props} style={placeholderStyle} ref="placeholderNode">
|
||||
<div class={className} ref="fixedNode" style={affixStyle}>
|
||||
{$slots.default}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ResizeObserver>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
@ -1,21 +0,0 @@
|
||||
## API
|
||||
|
||||
| 成员 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | |
|
||||
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | |
|
||||
| target | 设置 `Affix` 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | () => HTMLElement | () => window |
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
| -------- | ---------------------------- | ----------------- |
|
||||
| change | 固定状态改变时触发的回调函数 | Function(affixed) |
|
||||
|
||||
**注意:**`Affix` 内的元素不要使用绝对定位,如需要绝对定位的效果,可以直接设置 `Affix` 为绝对定位:
|
||||
|
||||
```html
|
||||
<a-affix :style="{ position: 'absolute', top: y, left: x}">
|
||||
...
|
||||
</a-affix>
|
||||
```
|
@ -1,4 +1,4 @@
|
||||
@import '../../style/themes/default';
|
||||
@import '../../style/themes/index';
|
||||
|
||||
.@{ant-prefix}-affix {
|
||||
position: fixed;
|
||||
|
88
components/affix/utils.js
Normal file
88
components/affix/utils.js
Normal file
@ -0,0 +1,88 @@
|
||||
import addEventListener from '../vc-util/Dom/addEventListener';
|
||||
|
||||
export function getTargetRect(target) {
|
||||
return target !== window
|
||||
? target.getBoundingClientRect()
|
||||
: { top: 0, bottom: window.innerHeight };
|
||||
}
|
||||
|
||||
export function getFixedTop(placeholderReact, targetRect, offsetTop) {
|
||||
if (offsetTop !== undefined && targetRect.top > placeholderReact.top - offsetTop) {
|
||||
return offsetTop + targetRect.top + 'px';
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getFixedBottom(placeholderReact, targetRect, offsetBottom) {
|
||||
if (offsetBottom !== undefined && targetRect.bottom < placeholderReact.bottom + offsetBottom) {
|
||||
const targetBottomOffset = window.innerHeight - targetRect.bottom;
|
||||
return offsetBottom + targetBottomOffset + 'px';
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// ======================== Observer ========================
|
||||
const TRIGGER_EVENTS = [
|
||||
'resize',
|
||||
'scroll',
|
||||
'touchstart',
|
||||
'touchmove',
|
||||
'touchend',
|
||||
'pageshow',
|
||||
'load',
|
||||
];
|
||||
|
||||
let observerEntities = [];
|
||||
|
||||
export function getObserverEntities() {
|
||||
// Only used in test env. Can be removed if refactor.
|
||||
return observerEntities;
|
||||
}
|
||||
|
||||
export function addObserveTarget(target, affix) {
|
||||
if (!target) return;
|
||||
|
||||
let entity = observerEntities.find(item => item.target === target);
|
||||
|
||||
if (entity) {
|
||||
entity.affixList.push(affix);
|
||||
} else {
|
||||
entity = {
|
||||
target,
|
||||
affixList: [affix],
|
||||
eventHandlers: {},
|
||||
};
|
||||
observerEntities.push(entity);
|
||||
|
||||
// Add listener
|
||||
TRIGGER_EVENTS.forEach(eventName => {
|
||||
entity.eventHandlers[eventName] = addEventListener(target, eventName, () => {
|
||||
entity.affixList.forEach(targetAffix => {
|
||||
targetAffix.lazyUpdatePosition();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function removeObserveTarget(affix) {
|
||||
const observerEntity = observerEntities.find(oriObserverEntity => {
|
||||
const hasAffix = oriObserverEntity.affixList.some(item => item === affix);
|
||||
if (hasAffix) {
|
||||
oriObserverEntity.affixList = oriObserverEntity.affixList.filter(item => item !== affix);
|
||||
}
|
||||
return hasAffix;
|
||||
});
|
||||
|
||||
if (observerEntity && observerEntity.affixList.length === 0) {
|
||||
observerEntities = observerEntities.filter(item => item !== observerEntity);
|
||||
|
||||
// Remove listener
|
||||
TRIGGER_EVENTS.forEach(eventName => {
|
||||
const handler = observerEntity.eventHandlers[eventName];
|
||||
if (handler && handler.remove) {
|
||||
handler.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/alert/demo/banner.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/alert/demo/banner.md correctly 1`] = `
|
||||
<div>
|
||||
<div data-show="true" class="ant-alert ant-alert-warning ant-alert-banner"><i aria-label="icon: exclamation-circle" class="ant-alert-icon anticon anticon-exclamation-circle"><svg viewBox="64 64 896 896" data-icon="exclamation-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
|
||||
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"></path>
|
||||
</svg></i><span class="ant-alert-message">Warning text</span><span class="ant-alert-description"></span></div> <br>
|
||||
<div data-show="true" class="ant-alert ant-alert-warning ant-alert-banner ant-alert-closable"><i aria-label="icon: exclamation-circle" class="ant-alert-icon anticon anticon-exclamation-circle"><svg viewBox="64 64 896 896" data-icon="exclamation-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
|
||||
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 0 1 0-96 48.01 48.01 0 0 1 0 96z"></path>
|
||||
</svg></i><span class="ant-alert-message">Very long warning text warning text text text text text text text</span><span class="ant-alert-description"></span><a class="ant-alert-close-icon"><i aria-label="icon: close" class="anticon anticon-close"><svg viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
|
||||
</svg></i><span class="ant-alert-message">Very long warning text warning text text text text text text text</span><span class="ant-alert-description"></span><a type="button" tabindex="0" class="ant-alert-close-icon"><i aria-label="icon: close" class="anticon anticon-close"><svg viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
|
||||
<path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
|
||||
</svg></i></a></div> <br>
|
||||
<div data-show="true" class="ant-alert ant-alert-warning ant-alert-no-icon ant-alert-banner"><span class="ant-alert-message">Warning text without icon</span><span class="ant-alert-description"></span></div> <br>
|
||||
@ -17,22 +17,22 @@ exports[`renders ./components/alert/demo/banner.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/basic.md correctly 1`] = `<div data-show="true" class="ant-alert ant-alert-success ant-alert-no-icon"><span class="ant-alert-message">Success Text</span><span class="ant-alert-description"></span></div>`;
|
||||
exports[`renders ./antdv-demo/alert/demo/basic.md correctly 1`] = `<div data-show="true" class="ant-alert ant-alert-success ant-alert-no-icon"><span class="ant-alert-message">Success Text</span><span class="ant-alert-description"></span></div>`;
|
||||
|
||||
exports[`renders ./components/alert/demo/closable.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/alert/demo/closable.md correctly 1`] = `
|
||||
<div>
|
||||
<div data-show="true" class="ant-alert ant-alert-warning ant-alert-no-icon ant-alert-closable"><span class="ant-alert-message">Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text</span><span class="ant-alert-description"></span><a class="ant-alert-close-icon"><i aria-label="icon: close" class="anticon anticon-close"><svg viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
|
||||
<div data-show="true" class="ant-alert ant-alert-warning ant-alert-no-icon ant-alert-closable"><span class="ant-alert-message">Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text</span><span class="ant-alert-description"></span><a type="button" tabindex="0" class="ant-alert-close-icon"><i aria-label="icon: close" class="anticon anticon-close"><svg viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
|
||||
<path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
|
||||
</svg></i></a></div>
|
||||
<div data-show="true" class="ant-alert ant-alert-error ant-alert-with-description ant-alert-no-icon ant-alert-closable"><span class="ant-alert-message">Error Text</span><span class="ant-alert-description">Error Description Error Description Error Description Error Description Error Description Error Description</span><a class="ant-alert-close-icon"><i aria-label="icon: close" class="anticon anticon-close"><svg viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
|
||||
<div data-show="true" class="ant-alert ant-alert-error ant-alert-with-description ant-alert-no-icon ant-alert-closable"><span class="ant-alert-message">Error Text</span><span class="ant-alert-description">Error Description Error Description Error Description Error Description Error Description Error Description</span><a type="button" tabindex="0" class="ant-alert-close-icon"><i aria-label="icon: close" class="anticon anticon-close"><svg viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
|
||||
<path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
|
||||
</svg></i></a></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/close-text.md correctly 1`] = `<div data-show="true" class="ant-alert ant-alert-info ant-alert-no-icon ant-alert-closable"><span class="ant-alert-message">Info Text</span><span class="ant-alert-description"></span><a class="ant-alert-close-icon">Close Now</a></div>`;
|
||||
exports[`renders ./antdv-demo/alert/demo/close-text.md correctly 1`] = `<div data-show="true" class="ant-alert ant-alert-info ant-alert-no-icon ant-alert-closable"><span class="ant-alert-message">Info Text</span><span class="ant-alert-description"></span><a type="button" tabindex="0" class="ant-alert-close-icon"><span class="ant-alert-close-text">Close Now</span></a></div>`;
|
||||
|
||||
exports[`renders ./components/alert/demo/custom-icon.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/alert/demo/custom-icon.md correctly 1`] = `
|
||||
<div>
|
||||
<div data-show="true" class="ant-alert ant-alert-success ant-alert-no-icon"><span class="ant-alert-message">showIcon = false</span><span class="ant-alert-description"></span></div>
|
||||
<div data-show="true" class="ant-alert ant-alert-success"><span class="ant-alert-icon"><i slot="icon" aria-label="icon: smile" class="anticon anticon-smile"><svg viewBox="64 64 896 896" data-icon="smile" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M288 421a48 48 0 1 0 96 0 48 48 0 1 0-96 0zm352 0a48 48 0 1 0 96 0 48 48 0 1 0-96 0zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm263 711c-34.2 34.2-74 61-118.3 79.8C611 874.2 562.3 884 512 884c-50.3 0-99-9.8-144.8-29.2A370.4 370.4 0 0 1 248.9 775c-34.2-34.2-61-74-79.8-118.3C149.8 611 140 562.3 140 512s9.8-99 29.2-144.8A370.4 370.4 0 0 1 249 248.9c34.2-34.2 74-61 118.3-79.8C413 149.8 461.7 140 512 140c50.3 0 99 9.8 144.8 29.2A370.4 370.4 0 0 1 775.1 249c34.2 34.2 61 74 79.8 118.3C874.2 413 884 461.7 884 512s-9.8 99-29.2 144.8A368.89 368.89 0 0 1 775 775zM664 533h-48.1c-4.2 0-7.8 3.2-8.1 7.4C604 589.9 562.5 629 512 629s-92.1-39.1-95.8-88.6c-.3-4.2-3.9-7.4-8.1-7.4H360a8 8 0 0 0-8 8.4c4.4 84.3 74.5 151.6 160 151.6s155.6-67.3 160-151.6a8 8 0 0 0-8-8.4z"></path></svg></i></span><span class="ant-alert-message">Success Tips</span><span class="ant-alert-description"></span></div>
|
||||
@ -46,7 +46,7 @@ exports[`renders ./components/alert/demo/custom-icon.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/description.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/alert/demo/description.md correctly 1`] = `
|
||||
<div>
|
||||
<div data-show="true" class="ant-alert ant-alert-success ant-alert-with-description ant-alert-no-icon"><span class="ant-alert-message">Success Text</span><span class="ant-alert-description"><p>
|
||||
Success Description <span style="color: red;">Success</span> Description Success Description
|
||||
@ -57,7 +57,7 @@ exports[`renders ./components/alert/demo/description.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/alert/demo/icon.md correctly 1`] = `
|
||||
<div>
|
||||
<div data-show="true" class="ant-alert ant-alert-success"><i aria-label="icon: check-circle" class="ant-alert-icon anticon anticon-check-circle"><svg viewBox="64 64 896 896" data-icon="check-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
|
||||
<path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 0 1-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"></path>
|
||||
@ -90,15 +90,15 @@ exports[`renders ./components/alert/demo/icon.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/smooth-closed.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/alert/demo/smooth-closed.md correctly 1`] = `
|
||||
<div>
|
||||
<div data-show="true" class="ant-alert ant-alert-success ant-alert-no-icon ant-alert-closable"><span class="ant-alert-message">Alert Message Text</span><span class="ant-alert-description"></span><a class="ant-alert-close-icon"><i aria-label="icon: close" class="anticon anticon-close"><svg viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
|
||||
<div data-show="true" class="ant-alert ant-alert-success ant-alert-no-icon ant-alert-closable"><span class="ant-alert-message">Alert Message Text</span><span class="ant-alert-description"></span><a type="button" tabindex="0" class="ant-alert-close-icon"><i aria-label="icon: close" class="anticon anticon-close"><svg viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class="">
|
||||
<path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
|
||||
</svg></i></a></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/alert/demo/style.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/alert/demo/style.md correctly 1`] = `
|
||||
<div>
|
||||
<div data-show="true" class="ant-alert ant-alert-success ant-alert-no-icon"><span class="ant-alert-message">Success Text</span><span class="ant-alert-description"></span></div>
|
||||
<div data-show="true" class="ant-alert ant-alert-info ant-alert-no-icon"><span class="ant-alert-message">Info Text</span><span class="ant-alert-description"></span></div>
|
||||
|
@ -28,10 +28,10 @@ describe('Alert', () => {
|
||||
},
|
||||
});
|
||||
wrapper.find('.ant-alert-close-icon').trigger('click');
|
||||
expect(onClose).toBeCalled();
|
||||
expect(onClose).toHaveBeenCalled();
|
||||
jest.runAllTimers();
|
||||
wrapper.vm.$refs.alert.animationEnd();
|
||||
expect(afterClose).toBeCalled();
|
||||
expect(afterClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('data and aria props', () => {
|
||||
|
@ -1,27 +0,0 @@
|
||||
<cn>
|
||||
#### 顶部公告
|
||||
页面顶部通告形式,默认有图标且`type` 为 'warning'。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Banner
|
||||
Display Alert as a banner at top of page.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-alert message="Warning text" banner />
|
||||
<br />
|
||||
<a-alert
|
||||
message="Very long warning text warning text text text text text text text"
|
||||
banner
|
||||
closable
|
||||
/>
|
||||
<br />
|
||||
<a-alert :showIcon="false" message="Warning text without icon" banner />
|
||||
<br />
|
||||
<a-alert type="error" message="Error text" banner />
|
||||
</div>
|
||||
</template>
|
||||
```
|
@ -1,15 +0,0 @@
|
||||
<cn>
|
||||
#### 基本
|
||||
最简单的用法,适用于简短的警告提示。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### basic
|
||||
The simplest usage for short messages.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<a-alert message="Success Text" type="success" />
|
||||
</template>
|
||||
```
|
@ -1,38 +0,0 @@
|
||||
<cn>
|
||||
#### 可关闭的警告提示
|
||||
显示关闭按钮,点击可关闭警告提示。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Closable
|
||||
To show close button.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-alert
|
||||
message="Warning Text Warning Text Warning TextW arning Text Warning Text Warning TextWarning Text"
|
||||
type="warning"
|
||||
closable
|
||||
@close="onClose"
|
||||
/>
|
||||
<a-alert
|
||||
message="Error Text"
|
||||
description="Error Description Error Description Error Description Error Description Error Description Error Description"
|
||||
type="error"
|
||||
closable
|
||||
@close="onClose"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
onClose(e) {
|
||||
console.log(e, 'I was closed.');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
@ -1,15 +0,0 @@
|
||||
<cn>
|
||||
#### 自定义关闭
|
||||
可以自定义关闭,自定义的文字会替换原先的关闭 `Icon`。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Customized Close Text
|
||||
Replace the default icon with customized text.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<a-alert message="Info Text" type="info" closeText="Close Now" />
|
||||
</template>
|
||||
```
|
@ -1,63 +0,0 @@
|
||||
<cn>
|
||||
#### 自定义图标
|
||||
可口的图标让信息类型更加醒目。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Custom Icon
|
||||
Decent icon make information more clear and more friendly.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-alert message="showIcon = false" type="success">
|
||||
<a-icon type="smile" slot="icon" />
|
||||
</a-alert>
|
||||
<a-alert message="Success Tips" type="success" showIcon>
|
||||
<a-icon type="smile" slot="icon" />
|
||||
</a-alert>
|
||||
<a-alert message="Informational Notes" type="info" showIcon>
|
||||
<a-icon type="smile" slot="icon" />
|
||||
</a-alert>
|
||||
<a-alert message="Warning" type="warning" showIcon>
|
||||
<a-icon type="smile" slot="icon" />
|
||||
</a-alert>
|
||||
<a-alert message="Error" type="error" showIcon>
|
||||
<a-icon type="smile" slot="icon" />
|
||||
</a-alert>
|
||||
<a-alert
|
||||
message="Success Tips"
|
||||
description="Detailed description and advices about successful copywriting."
|
||||
type="success"
|
||||
showIcon
|
||||
>
|
||||
<a-icon type="smile" slot="icon" />
|
||||
</a-alert>
|
||||
<a-alert
|
||||
message="Informational Notes"
|
||||
description="Additional description and informations about copywriting."
|
||||
type="info"
|
||||
showIcon
|
||||
>
|
||||
<a-icon type="smile" slot="icon" />
|
||||
</a-alert>
|
||||
<a-alert
|
||||
message="Warning"
|
||||
description="This is a warning notice about copywriting."
|
||||
type="warning"
|
||||
showIcon
|
||||
>
|
||||
<a-icon type="smile" slot="icon" />
|
||||
</a-alert>
|
||||
<a-alert
|
||||
message="Error"
|
||||
description="This is an error message about copywriting."
|
||||
type="error"
|
||||
showIcon
|
||||
>
|
||||
<a-icon type="smile" slot="icon" />
|
||||
</a-alert>
|
||||
</div>
|
||||
</template>
|
||||
```
|
@ -1,36 +0,0 @@
|
||||
<cn>
|
||||
#### 含有辅助性文字介绍
|
||||
含有辅助性文字介绍的警告提示。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Description
|
||||
Additional description for alert message.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-alert message="Success Text" type="success">
|
||||
<p slot="description">
|
||||
Success Description <span style="color: red">Success</span> Description Success Description
|
||||
</p>
|
||||
</a-alert>
|
||||
<a-alert
|
||||
message="Info Text"
|
||||
description="Info Description Info Description Info Description Info Description"
|
||||
type="info"
|
||||
/>
|
||||
<a-alert
|
||||
message="Warning Text"
|
||||
description="Warning Description Warning Description Warning Description Warning Description"
|
||||
type="warning"
|
||||
/>
|
||||
<a-alert
|
||||
message="Error Text"
|
||||
description="Error Description Error Description Error Description Error Description"
|
||||
type="error"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
```
|
@ -1,44 +0,0 @@
|
||||
<cn>
|
||||
#### 图标
|
||||
可口的图标让信息类型更加醒目。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Icon
|
||||
Decent icon make information more clear and more friendly.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-alert message="Success Tips" type="success" showIcon />
|
||||
<a-alert message="Informational Notes" type="info" showIcon />
|
||||
<a-alert message="Warning" type="warning" showIcon />
|
||||
<a-alert message="Error" type="error" showIcon />
|
||||
<a-alert
|
||||
message="Success Tips"
|
||||
description="Detailed description and advices about successful copywriting."
|
||||
type="success"
|
||||
showIcon
|
||||
/>
|
||||
<a-alert
|
||||
message="Informational Notes"
|
||||
description="Additional description and informations about copywriting."
|
||||
type="info"
|
||||
showIcon
|
||||
/>
|
||||
<a-alert
|
||||
message="Warning"
|
||||
description="This is a warning notice about copywriting."
|
||||
type="warning"
|
||||
showIcon
|
||||
/>
|
||||
<a-alert
|
||||
message="Error"
|
||||
description="This is an error message about copywriting."
|
||||
type="error"
|
||||
showIcon
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
```
|
@ -1,59 +0,0 @@
|
||||
<script>
|
||||
import Banner from './banner';
|
||||
import Basic from './basic';
|
||||
import Closable from './closable';
|
||||
import CloseText from './close-text';
|
||||
import Description from './description';
|
||||
import Icon from './icon';
|
||||
import Style from './style';
|
||||
import SmoothClosed from './smooth-closed';
|
||||
|
||||
import CN from '../index.zh-CN.md';
|
||||
import US from '../index.en-US.md';
|
||||
const md = {
|
||||
cn: `# Alert 警告提示
|
||||
警告提示,展现需要关注的信息。
|
||||
## 何时使用
|
||||
- 当某个页面需要向用户显示警告的信息时。
|
||||
- 非浮层的静态展现形式,始终展现,不会自动消失,用户可以点击关闭。
|
||||
## 代码演示`,
|
||||
us: `# Alert
|
||||
Alert component for feedback.
|
||||
## When To Use
|
||||
- When you need to show alert messages to users.
|
||||
- When you need a persistent static container which is closable by user actions.
|
||||
## Examples
|
||||
`,
|
||||
};
|
||||
export default {
|
||||
category: 'Components',
|
||||
subtitle: '警告提示',
|
||||
type: 'Feedback',
|
||||
zhType: '反馈',
|
||||
title: 'Alert',
|
||||
render() {
|
||||
return (
|
||||
<div id="components-alert-demo">
|
||||
<md cn={md.cn} us={md.us} />
|
||||
<Banner />
|
||||
<Basic />
|
||||
<Closable />
|
||||
<CloseText />
|
||||
<Description />
|
||||
<Icon />
|
||||
<Style />
|
||||
<SmoothClosed />
|
||||
<api>
|
||||
<CN slot="cn" />
|
||||
<US />
|
||||
</api>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
#components-alert-demo .ant-alert {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
</style>
|
@ -1,37 +0,0 @@
|
||||
<cn>
|
||||
#### 平滑地卸载
|
||||
平滑、自然的卸载提示。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Smoothly Unmount
|
||||
Smoothly and unaffectedly unmount Alert.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-alert
|
||||
v-if="visible"
|
||||
message="Alert Message Text"
|
||||
type="success"
|
||||
closable
|
||||
:afterClose="handleClose"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
visible: true,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.visible = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
@ -1,20 +0,0 @@
|
||||
<cn>
|
||||
#### 四种样式
|
||||
共有四种样式 `success`、`info`、`warning`、`error`。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### More types
|
||||
There are 4 types of Alert: `success`, `info`, `warning`, `error`.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-alert message="Success Text" type="success" />
|
||||
<a-alert message="Info Text" type="info" />
|
||||
<a-alert message="Warning Text" type="warning" />
|
||||
<a-alert message="Error Text" type="error" />
|
||||
</div>
|
||||
</template>
|
||||
```
|
@ -1,19 +0,0 @@
|
||||
## API
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| afterClose | Called when close animation is finished | () => void | - |
|
||||
| banner | Whether to show as banner | boolean | false |
|
||||
| closable | Whether Alert can be closed | boolean | - |
|
||||
| closeText | Close text to show | string\|slot | - |
|
||||
| description | Additional content of Alert | string\|slot | - |
|
||||
| icon | Custom icon, effective when `showIcon` is `true` | vnode \| slot | - |
|
||||
| message | Content of Alert | string\|slot | - |
|
||||
| showIcon | Whether to show icon | boolean | false, in `banner` mode default is true |
|
||||
| type | Type of Alert styles, options: `success`, `info`, `warning`, `error` | string | `info`, in `banner` mode default is `warning` |
|
||||
|
||||
### events
|
||||
|
||||
| Events Name | Description | Arguments |
|
||||
| ----------- | ----------------------------- | ----------------------- |
|
||||
| close | Callback when Alert is closed | (e: MouseEvent) => void |
|
@ -7,6 +7,7 @@ import { getComponentFromProp, isValidElement } from '../_util/props-util';
|
||||
import { cloneElement } from '../_util/vnode';
|
||||
import { ConfigConsumerProps } from '../config-provider';
|
||||
import Base from '../base';
|
||||
|
||||
function noop() {}
|
||||
export const AlertProps = {
|
||||
/**
|
||||
@ -42,7 +43,7 @@ const Alert = {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
closing: true,
|
||||
closing: false,
|
||||
closed: false,
|
||||
};
|
||||
},
|
||||
@ -56,14 +57,14 @@ const Alert = {
|
||||
dom.style.height = `${dom.offsetHeight}px`;
|
||||
|
||||
this.setState({
|
||||
closing: false,
|
||||
closing: true,
|
||||
});
|
||||
this.$emit('close', e);
|
||||
},
|
||||
animationEnd() {
|
||||
this.setState({
|
||||
closing: false,
|
||||
closed: true,
|
||||
closing: true,
|
||||
});
|
||||
this.afterClose();
|
||||
},
|
||||
@ -84,8 +85,7 @@ const Alert = {
|
||||
// banner模式默认为警告
|
||||
type = banner && type === undefined ? 'warning' : type || 'info';
|
||||
let iconTheme = 'filled';
|
||||
// should we give a warning?
|
||||
// warning(!iconType, `The property 'iconType' is deprecated. Use the property 'icon' instead.`);
|
||||
|
||||
if (!iconType) {
|
||||
switch (type) {
|
||||
case 'success':
|
||||
@ -117,7 +117,7 @@ const Alert = {
|
||||
|
||||
const alertCls = classNames(prefixCls, {
|
||||
[`${prefixCls}-${type}`]: true,
|
||||
[`${prefixCls}-close`]: !closing,
|
||||
[`${prefixCls}-closing`]: closing,
|
||||
[`${prefixCls}-with-description`]: !!description,
|
||||
[`${prefixCls}-no-icon`]: !showIcon,
|
||||
[`${prefixCls}-banner`]: !!banner,
|
||||
@ -125,8 +125,12 @@ const Alert = {
|
||||
});
|
||||
|
||||
const closeIcon = closable ? (
|
||||
<a onClick={this.handleClose} class={`${prefixCls}-close-icon`}>
|
||||
{closeText || <Icon type="close" />}
|
||||
<a type="button" onClick={this.handleClose} class={`${prefixCls}-close-icon`} tabIndex={0}>
|
||||
{closeText ? (
|
||||
<span class={`${prefixCls}-close-text`}>{closeText}</span>
|
||||
) : (
|
||||
<Icon type="close" />
|
||||
)}
|
||||
</a>
|
||||
) : null;
|
||||
|
||||
@ -145,7 +149,7 @@ const Alert = {
|
||||
});
|
||||
return closed ? null : (
|
||||
<transition {...transitionProps}>
|
||||
<div v-show={closing} class={alertCls} data-show={closing}>
|
||||
<div v-show={!closing} class={alertCls} data-show={!closing}>
|
||||
{showIcon ? iconNode : null}
|
||||
<span class={`${prefixCls}-message`}>{message}</span>
|
||||
<span class={`${prefixCls}-description`}>{description}</span>
|
||||
|
@ -1,19 +0,0 @@
|
||||
## API
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - |
|
||||
| banner | 是否用作顶部公告 | boolean | false |
|
||||
| closable | 默认不显示关闭按钮 | boolean | 无 |
|
||||
| closeText | 自定义关闭按钮 | string\|slot | 无 |
|
||||
| description | 警告提示的辅助性文字介绍 | string\|slot | 无 |
|
||||
| icon | 自定义图标,`showIcon` 为 `true` 时有效 | vnode \| slot | - |
|
||||
| message | 警告提示内容 | string\|slot | 无 |
|
||||
| showIcon | 是否显示辅助图标 | boolean | false,`banner` 模式下默认值为 true |
|
||||
| type | 指定警告提示的样式,有四种选择 `success`、`info`、`warning`、`error` | string | `info`,`banner` 模式下默认值为 `warning` |
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
| -------- | -------------------- | ----------------------- |
|
||||
| close | 关闭时触发的回调函数 | (e: MouseEvent) => void |
|
@ -1,4 +1,4 @@
|
||||
@import '../../style/themes/default';
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@alert-prefix-cls: ~'@{ant-prefix}-alert';
|
||||
@ -10,8 +10,10 @@
|
||||
|
||||
.@{alert-prefix-cls} {
|
||||
.reset-component;
|
||||
|
||||
position: relative;
|
||||
padding: 8px 15px 8px 37px;
|
||||
word-wrap: break-word;
|
||||
border-radius: @border-radius-base;
|
||||
|
||||
&&-no-icon {
|
||||
@ -70,9 +72,13 @@
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 16px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
font-size: @font-size-sm;
|
||||
line-height: 22px;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
|
||||
.@{iconfont-css-prefix}-close {
|
||||
@ -85,8 +91,11 @@
|
||||
}
|
||||
|
||||
&-close-text {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
color: @alert-close-color;
|
||||
transition: color 0.3s;
|
||||
&:hover {
|
||||
color: @alert-close-hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
&-with-description {
|
||||
@ -123,11 +132,15 @@
|
||||
font-size: @font-size-lg;
|
||||
}
|
||||
|
||||
&-message {
|
||||
color: @alert-message-color;
|
||||
}
|
||||
|
||||
&-with-description &-description {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&&-close {
|
||||
&&-closing {
|
||||
height: 0 !important;
|
||||
margin: 0;
|
||||
padding-top: 0;
|
||||
|
@ -1,9 +1,9 @@
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import classNames from 'classnames';
|
||||
import addEventListener from '../_util/Dom/addEventListener';
|
||||
import addEventListener from '../vc-util/Dom/addEventListener';
|
||||
import Affix from '../affix';
|
||||
import scrollTo from '../_util/scrollTo';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import raf from 'raf';
|
||||
import { initDefaultProps } from '../_util/props-util';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import { ConfigConsumerProps } from '../config-provider';
|
||||
@ -34,47 +34,47 @@ function getOffsetTop(element, container) {
|
||||
return rect.top;
|
||||
}
|
||||
|
||||
function easeInOutCubic(t, b, c, d) {
|
||||
const cc = c - b;
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return (cc / 2) * t * t * t + b;
|
||||
}
|
||||
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
|
||||
}
|
||||
// function easeInOutCubic(t, b, c, d) {
|
||||
// const cc = c - b;
|
||||
// t /= d / 2;
|
||||
// if (t < 1) {
|
||||
// return (cc / 2) * t * t * t + b;
|
||||
// }
|
||||
// return (cc / 2) * ((t -= 2) * t * t + 2) + b;
|
||||
// }
|
||||
|
||||
const sharpMatcherRegx = /#([^#]+)$/;
|
||||
function scrollTo(href, offsetTop = 0, getContainer, callback = () => {}) {
|
||||
const container = getContainer();
|
||||
const scrollTop = getScroll(container, true);
|
||||
const sharpLinkMatch = sharpMatcherRegx.exec(href);
|
||||
if (!sharpLinkMatch) {
|
||||
return;
|
||||
}
|
||||
const targetElement = document.getElementById(sharpLinkMatch[1]);
|
||||
if (!targetElement) {
|
||||
return;
|
||||
}
|
||||
const eleOffsetTop = getOffsetTop(targetElement, container);
|
||||
const targetScrollTop = scrollTop + eleOffsetTop - offsetTop;
|
||||
const startTime = Date.now();
|
||||
const frameFunc = () => {
|
||||
const timestamp = Date.now();
|
||||
const time = timestamp - startTime;
|
||||
const nextScrollTop = easeInOutCubic(time, scrollTop, targetScrollTop, 450);
|
||||
if (container === window) {
|
||||
window.scrollTo(window.pageXOffset, nextScrollTop);
|
||||
} else {
|
||||
container.scrollTop = nextScrollTop;
|
||||
}
|
||||
if (time < 450) {
|
||||
raf(frameFunc);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
raf(frameFunc);
|
||||
}
|
||||
// function scrollTo(href, offsetTop = 0, getContainer, callback = () => {}) {
|
||||
// const container = getContainer();
|
||||
// const scrollTop = getScroll(container, true);
|
||||
// const sharpLinkMatch = sharpMatcherRegx.exec(href);
|
||||
// if (!sharpLinkMatch) {
|
||||
// return;
|
||||
// }
|
||||
// const targetElement = document.getElementById(sharpLinkMatch[1]);
|
||||
// if (!targetElement) {
|
||||
// return;
|
||||
// }
|
||||
// const eleOffsetTop = getOffsetTop(targetElement, container);
|
||||
// const targetScrollTop = scrollTop + eleOffsetTop - offsetTop;
|
||||
// const startTime = Date.now();
|
||||
// const frameFunc = () => {
|
||||
// const timestamp = Date.now();
|
||||
// const time = timestamp - startTime;
|
||||
// const nextScrollTop = easeInOutCubic(time, scrollTop, targetScrollTop, 450);
|
||||
// if (container === window) {
|
||||
// window.scrollTo(window.pageXOffset, nextScrollTop);
|
||||
// } else {
|
||||
// container.scrollTop = nextScrollTop;
|
||||
// }
|
||||
// if (time < 450) {
|
||||
// raf(frameFunc);
|
||||
// } else {
|
||||
// callback();
|
||||
// }
|
||||
// };
|
||||
// raf(frameFunc);
|
||||
// }
|
||||
|
||||
export const AnchorProps = {
|
||||
prefixCls: PropTypes.string,
|
||||
@ -85,6 +85,8 @@ export const AnchorProps = {
|
||||
getContainer: PropTypes.func,
|
||||
wrapperClass: PropTypes.string,
|
||||
wrapperStyle: PropTypes.object,
|
||||
getCurrentAnchor: PropTypes.func,
|
||||
targetOffset: PropTypes.number,
|
||||
};
|
||||
|
||||
export default {
|
||||
@ -130,43 +132,38 @@ export default {
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
const { getContainer } = this;
|
||||
this.scrollEvent = addEventListener(getContainer(), 'scroll', this.handleScroll);
|
||||
this.scrollContainer = getContainer();
|
||||
this.scrollEvent = addEventListener(this.scrollContainer, 'scroll', this.handleScroll);
|
||||
this.handleScroll();
|
||||
});
|
||||
},
|
||||
|
||||
updated() {
|
||||
this.$nextTick(() => {
|
||||
if (this.scrollEvent) {
|
||||
const { getContainer } = this;
|
||||
const currentContainer = getContainer();
|
||||
if (this.scrollContainer !== currentContainer) {
|
||||
this.scrollContainer = currentContainer;
|
||||
this.scrollEvent.remove();
|
||||
this.scrollEvent = addEventListener(this.scrollContainer, 'scroll', this.handleScroll);
|
||||
this.handleScroll();
|
||||
}
|
||||
}
|
||||
this.updateInk();
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.scrollEvent) {
|
||||
this.scrollEvent.remove();
|
||||
}
|
||||
},
|
||||
|
||||
updated() {
|
||||
this.$nextTick(() => {
|
||||
this.updateInk();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
handleScroll() {
|
||||
if (this.animating) {
|
||||
return;
|
||||
getCurrentActiveLink(offsetTop = 0, bounds = 5) {
|
||||
const { getCurrentAnchor } = this;
|
||||
|
||||
if (typeof getCurrentAnchor === 'function') {
|
||||
return getCurrentAnchor();
|
||||
}
|
||||
const { offsetTop, bounds } = this;
|
||||
this.setState({
|
||||
activeLink: this.getCurrentAnchor(offsetTop, bounds),
|
||||
});
|
||||
},
|
||||
|
||||
handleScrollTo(link) {
|
||||
const { offsetTop, getContainer } = this;
|
||||
this.animating = true;
|
||||
this.setState({ activeLink: link });
|
||||
scrollTo(link, offsetTop, getContainer, () => {
|
||||
this.animating = false;
|
||||
});
|
||||
},
|
||||
|
||||
getCurrentAnchor(offsetTop = 0, bounds = 5) {
|
||||
const activeLink = '';
|
||||
if (typeof document === 'undefined') {
|
||||
return activeLink;
|
||||
@ -199,6 +196,56 @@ export default {
|
||||
return '';
|
||||
},
|
||||
|
||||
handleScrollTo(link) {
|
||||
const { offsetTop, getContainer, targetOffset } = this;
|
||||
|
||||
this.setCurrentActiveLink(link);
|
||||
const container = getContainer();
|
||||
const scrollTop = getScroll(container, true);
|
||||
const sharpLinkMatch = sharpMatcherRegx.exec(link);
|
||||
if (!sharpLinkMatch) {
|
||||
return;
|
||||
}
|
||||
const targetElement = document.getElementById(sharpLinkMatch[1]);
|
||||
if (!targetElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const eleOffsetTop = getOffsetTop(targetElement, container);
|
||||
let y = scrollTop + eleOffsetTop;
|
||||
y -= targetOffset !== undefined ? targetOffset : offsetTop || 0;
|
||||
this.animating = true;
|
||||
|
||||
scrollTo(y, {
|
||||
callback: () => {
|
||||
this.animating = false;
|
||||
},
|
||||
getContainer,
|
||||
});
|
||||
},
|
||||
setCurrentActiveLink(link) {
|
||||
const { activeLink } = this;
|
||||
|
||||
if (activeLink !== link) {
|
||||
this.setState({
|
||||
activeLink: link,
|
||||
});
|
||||
this.$emit('change', link);
|
||||
}
|
||||
},
|
||||
|
||||
handleScroll() {
|
||||
if (this.animating) {
|
||||
return;
|
||||
}
|
||||
const { offsetTop, bounds, targetOffset } = this;
|
||||
const currentActiveLink = this.getCurrentActiveLink(
|
||||
targetOffset !== undefined ? targetOffset : offsetTop || 0,
|
||||
bounds,
|
||||
);
|
||||
this.setCurrentActiveLink(currentActiveLink);
|
||||
},
|
||||
|
||||
updateInk() {
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
@ -206,7 +253,7 @@ export default {
|
||||
const { _sPrefixCls } = this;
|
||||
const linkNode = this.$el.getElementsByClassName(`${_sPrefixCls}-link-title-active`)[0];
|
||||
if (linkNode) {
|
||||
this.$refs.linkNode.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2 - 4.5}px`;
|
||||
this.$refs.inkNode.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2 - 4.5}px`;
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -245,7 +292,7 @@ export default {
|
||||
<div class={wrapperClass} style={wrapperStyle}>
|
||||
<div class={anchorClass}>
|
||||
<div class={`${prefixCls}-ink`}>
|
||||
<span class={inkClass} ref="linkNode" />
|
||||
<span class={inkClass} ref="inkNode" />
|
||||
</div>
|
||||
{$slots.default}
|
||||
</div>
|
||||
|
@ -7,6 +7,7 @@ export const AnchorLinkProps = {
|
||||
prefixCls: PropTypes.string,
|
||||
href: PropTypes.string,
|
||||
title: PropTypes.any,
|
||||
target: PropTypes.string,
|
||||
};
|
||||
|
||||
export default {
|
||||
@ -47,7 +48,7 @@ export default {
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const { prefixCls: customizePrefixCls, href, $slots } = this;
|
||||
const { prefixCls: customizePrefixCls, href, $slots, target } = this;
|
||||
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
|
||||
@ -66,6 +67,7 @@ export default {
|
||||
class={titleClassName}
|
||||
href={href}
|
||||
title={typeof title === 'string' ? title : ''}
|
||||
target={target}
|
||||
onClick={this.handleClick}
|
||||
>
|
||||
{title}
|
||||
|
@ -1,6 +1,81 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/anchor/demo/basic.md correctly 1`] = `
|
||||
<div>
|
||||
<div class="">
|
||||
<div class="ant-anchor-wrapper" style="max-height: 100vh;">
|
||||
<div class="ant-anchor">
|
||||
<div class="ant-anchor-ink"><span class="ant-anchor-ink-ball"></span></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-basic" title="Basic demo" class="ant-anchor-link-title">Basic demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-static" title="Static demo" class="ant-anchor-link-title">Static demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-basic" title="Basic demo with Target" target="_blank" class="ant-anchor-link-title">Basic demo with Target</a></div>
|
||||
<div class="ant-anchor-link"><a href="#API" title="API" class="ant-anchor-link-title">API</a>
|
||||
<div class="ant-anchor-link"><a href="#Anchor-Props" title="Anchor Props" class="ant-anchor-link-title">Anchor Props</a></div>
|
||||
<div class="ant-anchor-link"><a href="#Link-Props" title="Link Props" class="ant-anchor-link-title">Link Props</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./antdv-demo/anchor/demo/customizeHighlight.md correctly 1`] = `
|
||||
<div class="ant-anchor-wrapper" style="max-height: 100vh;">
|
||||
<div class="ant-anchor fixed">
|
||||
<div class="ant-anchor-ink"><span class="ant-anchor-ink-ball"></span></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-basic" title="Basic demo" class="ant-anchor-link-title">Basic demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-static" title="Static demo" class="ant-anchor-link-title">Static demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#API" title="API" class="ant-anchor-link-title">API</a>
|
||||
<div class="ant-anchor-link"><a href="#Anchor-Props" title="Anchor Props" class="ant-anchor-link-title">Anchor Props</a></div>
|
||||
<div class="ant-anchor-link"><a href="#Link-Props" title="Link Props" class="ant-anchor-link-title">Link Props</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./antdv-demo/anchor/demo/onChange.md correctly 1`] = `
|
||||
<div class="ant-anchor-wrapper" style="max-height: 100vh;">
|
||||
<div class="ant-anchor fixed">
|
||||
<div class="ant-anchor-ink"><span class="ant-anchor-ink-ball"></span></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-basic" title="Basic demo" class="ant-anchor-link-title">Basic demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-static" title="Static demo" class="ant-anchor-link-title">Static demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#API" title="API" class="ant-anchor-link-title">API</a>
|
||||
<div class="ant-anchor-link"><a href="#Anchor-Props" title="Anchor Props" class="ant-anchor-link-title">Anchor Props</a></div>
|
||||
<div class="ant-anchor-link"><a href="#Link-Props" title="Link Props" class="ant-anchor-link-title">Link Props</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./antdv-demo/anchor/demo/onClick.md correctly 1`] = `
|
||||
<div class="ant-anchor-wrapper" style="max-height: 100vh;">
|
||||
<div class="ant-anchor fixed">
|
||||
<div class="ant-anchor-ink"><span class="ant-anchor-ink-ball"></span></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-basic" title="Basic demo" class="ant-anchor-link-title">Basic demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-static" title="Static demo" class="ant-anchor-link-title">Static demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#API" title="API" class="ant-anchor-link-title">API</a>
|
||||
<div class="ant-anchor-link"><a href="#Anchor-Props" title="Anchor Props" class="ant-anchor-link-title">Anchor Props</a></div>
|
||||
<div class="ant-anchor-link"><a href="#Link-Props" title="Link Props" class="ant-anchor-link-title">Link Props</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./antdv-demo/anchor/demo/static.md correctly 1`] = `
|
||||
<div class="ant-anchor-wrapper" style="max-height: 100vh;">
|
||||
<div class="ant-anchor fixed">
|
||||
<div class="ant-anchor-ink"><span class="ant-anchor-ink-ball"></span></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-basic" title="Basic demo" class="ant-anchor-link-title">Basic demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-static" title="Static demo" class="ant-anchor-link-title">Static demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#API" title="API" class="ant-anchor-link-title">API</a>
|
||||
<div class="ant-anchor-link"><a href="#Anchor-Props" title="Anchor Props" class="ant-anchor-link-title">Anchor Props</a></div>
|
||||
<div class="ant-anchor-link"><a href="#Link-Props" title="Link Props" class="ant-anchor-link-title">Link Props</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./antdv-demo/anchor/demo/targetOffset.md correctly 1`] = `
|
||||
<div>
|
||||
<div class="">
|
||||
<div class="ant-anchor-wrapper" style="max-height: 100vh;">
|
||||
@ -17,31 +92,3 @@ exports[`renders ./components/anchor/demo/basic.md correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/anchor/demo/onClick.md correctly 1`] = `
|
||||
<div class="ant-anchor-wrapper" style="max-height: 100vh;">
|
||||
<div class="ant-anchor fixed">
|
||||
<div class="ant-anchor-ink"><span class="ant-anchor-ink-ball"></span></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-basic" title="Basic demo" class="ant-anchor-link-title">Basic demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-static" title="Static demo" class="ant-anchor-link-title">Static demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#API" title="API" class="ant-anchor-link-title">API</a>
|
||||
<div class="ant-anchor-link"><a href="#Anchor-Props" title="Anchor Props" class="ant-anchor-link-title">Anchor Props</a></div>
|
||||
<div class="ant-anchor-link"><a href="#Link-Props" title="Link Props" class="ant-anchor-link-title">Link Props</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/anchor/demo/static.md correctly 1`] = `
|
||||
<div class="ant-anchor-wrapper" style="max-height: 100vh;">
|
||||
<div class="ant-anchor fixed">
|
||||
<div class="ant-anchor-ink"><span class="ant-anchor-ink-ball"></span></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-basic" title="Basic demo" class="ant-anchor-link-title">Basic demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#components-anchor-demo-static" title="Static demo" class="ant-anchor-link-title">Static demo</a></div>
|
||||
<div class="ant-anchor-link"><a href="#API" title="API" class="ant-anchor-link-title">API</a>
|
||||
<div class="ant-anchor-link"><a href="#Anchor-Props" title="Anchor Props" class="ant-anchor-link-title">Anchor Props</a></div>
|
||||
<div class="ant-anchor-link"><a href="#Link-Props" title="Link Props" class="ant-anchor-link-title">Link Props</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -1,22 +0,0 @@
|
||||
<cn>
|
||||
#### 基本
|
||||
最简单的用法。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### basic
|
||||
The simplest usage.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<a-anchor>
|
||||
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
|
||||
<a-anchor-link href="#API" title="API">
|
||||
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
|
||||
<a-anchor-link href="#Link-Props" title="Link Props" />
|
||||
</a-anchor-link>
|
||||
</a-anchor>
|
||||
</template>
|
||||
```
|
@ -1,53 +0,0 @@
|
||||
<script>
|
||||
import Basic from './basic';
|
||||
import Static from './static';
|
||||
import OnClick from './onClick';
|
||||
|
||||
import CN from '../index.zh-CN.md';
|
||||
import US from '../index.en-US.md';
|
||||
const md = {
|
||||
cn: `# Anchor 锚点
|
||||
用于跳转到页面指定位置。
|
||||
|
||||
## 何时使用
|
||||
|
||||
需要展现当前页面上可供跳转的锚点链接,以及快速在锚点之间跳转。
|
||||
## 代码演示`,
|
||||
us: `# Anchor
|
||||
|
||||
Hyperlinks to scroll on one page.
|
||||
|
||||
## When To Use
|
||||
|
||||
For displaying anchor hyperlinks on page and jumping between them.
|
||||
## Examples
|
||||
`,
|
||||
};
|
||||
export default {
|
||||
category: 'Components',
|
||||
subtitle: '锚点',
|
||||
cols: 2,
|
||||
type: 'Other',
|
||||
zhType: '其他',
|
||||
title: 'Anchor',
|
||||
render() {
|
||||
return (
|
||||
<div id="components-anchor-demo">
|
||||
<md cn={md.cn} us={md.us} />
|
||||
<Basic />
|
||||
<Static />
|
||||
<OnClick />
|
||||
<api>
|
||||
<CN slot="cn" />
|
||||
<US />
|
||||
</api>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
#components-anchor-demo .ant-affix {
|
||||
z-index: 11;
|
||||
}
|
||||
</style>
|
@ -1,32 +0,0 @@
|
||||
<cn>
|
||||
#### 自定义 click 事件
|
||||
点击锚点不记录历史。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Customize the click event
|
||||
Clicking on an anchor does not record history.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<a-anchor :affix="false" @click="handleClick">
|
||||
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
|
||||
<a-anchor-link href="#API" title="API">
|
||||
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
|
||||
<a-anchor-link href="#Link-Props" title="Link Props" />
|
||||
</a-anchor-link>
|
||||
</a-anchor>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
handleClick(e, link) {
|
||||
e.preventDefault();
|
||||
console.log(link);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
@ -1,22 +0,0 @@
|
||||
<cn>
|
||||
#### 静态位置
|
||||
不浮动,状态不随页面滚动变化。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Static Anchor
|
||||
Do not change state when page is scrolling.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<a-anchor :affix="false">
|
||||
<a-anchor-link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<a-anchor-link href="#components-anchor-demo-static" title="Static demo" />
|
||||
<a-anchor-link href="#API" title="API">
|
||||
<a-anchor-link href="#Anchor-Props" title="Anchor Props" />
|
||||
<a-anchor-link href="#Link-Props" title="Link Props" />
|
||||
</a-anchor-link>
|
||||
</a-anchor>
|
||||
</template>
|
||||
```
|
@ -1,27 +0,0 @@
|
||||
## API
|
||||
|
||||
### Anchor Props
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| affix | Fixed mode of Anchor | boolean | true |
|
||||
| bounds | Bounding distance of anchor area | number | 5(px) |
|
||||
| getContainer | Scrolling container | () => HTMLElement | () => window |
|
||||
| offsetBottom | Pixels to offset from bottom when calculating position of scroll | number | - |
|
||||
| offsetTop | Pixels to offset from top when calculating position of scroll | number | 0 |
|
||||
| showInkInFixed | Whether show ink-balls in Fixed mode | boolean | false |
|
||||
| wrapperClass | The class name of the container | string | - |
|
||||
| wrapperStyle | The style of the container | object | - |
|
||||
|
||||
### Events
|
||||
|
||||
| Events Name | Description | Arguments |
|
||||
| ----------- | --------------------------------------- | -------------------------------- |
|
||||
| click | set the handler to handle `click` event | Function(e: Event, link: Object) |
|
||||
|
||||
### Link Props
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| -------- | -------------------- | ------------ | ------- |
|
||||
| href | target of hyperlink | string | |
|
||||
| title | content of hyperlink | string\|slot | |
|
@ -1,27 +0,0 @@
|
||||
## API
|
||||
|
||||
### Anchor Props
|
||||
|
||||
| 成员 | 说明 | 类型 | 默认值 |
|
||||
| -------------- | -------------------------------- | ----------------- | ------------ |
|
||||
| affix | 固定模式 | boolean | true |
|
||||
| bounds | 锚点区域边界 | number | 5(px) |
|
||||
| getContainer | 指定滚动的容器 | () => HTMLElement | () => window |
|
||||
| offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | |
|
||||
| offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | |
|
||||
| showInkInFixed | 固定模式是否显示小圆点 | boolean | false |
|
||||
| wrapperClass | 容器的类名 | string | - |
|
||||
| wrapperStyle | 容器样式 | object | - |
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
| -------- | ---------------------- | -------------------------------- |
|
||||
| click | `click` 事件的 handler | Function(e: Event, link: Object) |
|
||||
|
||||
### Link Props
|
||||
|
||||
| 成员 | 说明 | 类型 | 默认值 |
|
||||
| ----- | -------- | ------------ | ------ |
|
||||
| href | 锚点链接 | string | |
|
||||
| title | 文字内容 | string\|slot | |
|
@ -1,2 +1,5 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
|
||||
// style dependencies
|
||||
import '../../affix/style';
|
||||
|
@ -1,10 +1,11 @@
|
||||
@import '../../style/themes/default';
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@anchor-border-width: 2px;
|
||||
|
||||
.@{ant-prefix}-anchor {
|
||||
.reset-component;
|
||||
|
||||
position: relative;
|
||||
padding-left: @anchor-border-width;
|
||||
|
||||
@ -26,7 +27,7 @@
|
||||
width: @anchor-border-width;
|
||||
height: 100%;
|
||||
margin: 0 auto;
|
||||
background-color: @border-color-split;
|
||||
background-color: @anchor-border-color;
|
||||
content: ' ';
|
||||
}
|
||||
&-ball {
|
||||
|
@ -20,17 +20,6 @@ export default {
|
||||
disabled: PropTypes.bool,
|
||||
placeholder: PropTypes.string,
|
||||
},
|
||||
methods: {
|
||||
focus() {
|
||||
const ele = this.$refs.ele;
|
||||
ele.focus ? ele.focus() : this.$el.focus();
|
||||
},
|
||||
blur() {
|
||||
const ele = this.$refs.ele;
|
||||
ele.blur ? ele.blur() : this.$el.blur();
|
||||
},
|
||||
},
|
||||
|
||||
render() {
|
||||
const { $slots = {}, $attrs = {}, placeholder } = this;
|
||||
const listeners = getListeners(this);
|
||||
|
@ -1,20 +1,33 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/basic.md correctly 1`] = `
|
||||
<div tabindex="0" class="ant-select ant-select-combobox ant-select-enabled ant-select-show-search ant-select-auto-complete" style="width: 200px;">
|
||||
<div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single">
|
||||
<div class="ant-select-selection__rendered">
|
||||
<ul>
|
||||
<li class="ant-select-search ant-select-search--inline">
|
||||
<div class="ant-select-search__field__wrap"><input placeholder="input here" type="text" value="" class="ant-input ant-select-search__field"><span class="ant-select-search__field__mirror"> </span></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span>
|
||||
exports[`renders ./antdv-demo/auto-complete/demo/basic.md correctly 1`] = `
|
||||
<div>
|
||||
<div tabindex="0" class="ant-select ant-select-combobox ant-select-enabled ant-select-show-search ant-select-auto-complete" style="width: 200px;">
|
||||
<div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single">
|
||||
<div class="ant-select-selection__rendered">
|
||||
<ul>
|
||||
<li class="ant-select-search ant-select-search--inline">
|
||||
<div class="ant-select-search__field__wrap"><input placeholder="input here" type="text" value="" class="ant-input ant-select-search__field"><span class="ant-select-search__field__mirror"> </span></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span>
|
||||
</div>
|
||||
</div> <br> <br>
|
||||
<div tabindex="0" class="ant-select ant-select-combobox ant-select-enabled ant-select-show-search ant-select-auto-complete" style="width: 200px;">
|
||||
<div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single">
|
||||
<div class="ant-select-selection__rendered">
|
||||
<ul>
|
||||
<li class="ant-select-search ant-select-search--inline">
|
||||
<div class="ant-select-search__field__wrap"><input placeholder="control mode" type="text" value="" class="ant-input ant-select-search__field"><span class="ant-select-search__field__mirror"> </span></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/auto-complete/demo/certain-category.md correctly 1`] = `
|
||||
<div class="certain-category-search-wrapper" style="width: 250px;">
|
||||
<div tabindex="0" class="certain-category-search ant-select ant-select-combobox ant-select-enabled ant-select-lg ant-select-lg ant-select-show-search ant-select-auto-complete" style="width: 100%;">
|
||||
<div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single">
|
||||
@ -30,13 +43,13 @@ exports[`renders ./components/auto-complete/demo/certain-category.md correctly 1
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/auto-complete/demo/custom.md correctly 1`] = `
|
||||
<div tabindex="0" class="ant-select ant-select-combobox ant-select-enabled ant-select-show-search ant-select-auto-complete" style="width: 200px;">
|
||||
<div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single">
|
||||
<div class="ant-select-selection__rendered">
|
||||
<ul>
|
||||
<li class="ant-select-search ant-select-search--inline">
|
||||
<div class="ant-select-search__field__wrap"><textarea placeholder="input here" value="" class="custom ant-input ant-select-search__field" style="height: 50px;"></textarea><span class="ant-select-search__field__mirror"> </span></div>
|
||||
<div class="ant-select-search__field__wrap"><textarea placeholder="input here" class="custom ant-input ant-select-search__field" style="height: 50px;" value=""></textarea><span class="ant-select-search__field__mirror"> </span></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span>
|
||||
@ -44,7 +57,7 @@ exports[`renders ./components/auto-complete/demo/custom.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/auto-complete/demo/non-case-sensitive.md correctly 1`] = `
|
||||
<div tabindex="0" class="ant-select ant-select-combobox ant-select-enabled ant-select-show-search ant-select-auto-complete" style="width: 200px;">
|
||||
<div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single">
|
||||
<div class="ant-select-selection__rendered">
|
||||
@ -58,7 +71,7 @@ exports[`renders ./components/auto-complete/demo/non-case-sensitive.md correctly
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/auto-complete/demo/options.md correctly 1`] = `
|
||||
<div tabindex="0" class="ant-select ant-select-combobox ant-select-enabled ant-select-show-search ant-select-auto-complete" style="width: 200px;">
|
||||
<div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single">
|
||||
<div class="ant-select-selection__rendered">
|
||||
@ -72,14 +85,14 @@ exports[`renders ./components/auto-complete/demo/options.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/auto-complete/demo/uncertain-category.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/auto-complete/demo/uncertain-category.md correctly 1`] = `
|
||||
<div class="global-search-wrapper" style="width: 300px;">
|
||||
<div tabindex="0" class="global-search ant-select ant-select-combobox ant-select-enabled ant-select-lg ant-select-lg ant-select-show-search ant-select-auto-complete" style="width: 100%;">
|
||||
<div role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-controls="test-uuid" class="ant-select-selection ant-select-selection--single">
|
||||
<div class="ant-select-selection__rendered">
|
||||
<ul>
|
||||
<li class="ant-select-search ant-select-search--inline">
|
||||
<div class="ant-select-search__field__wrap"><span class="ant-input-affix-wrapper ant-select-search__field"><input placeholder="input here" type="text" value="" class="ant-input"><span class="ant-input-suffix"><button type="button" class="search-btn ant-btn ant-btn-primary ant-btn-lg"><i aria-label="icon: search" class="anticon anticon-search"><svg viewBox="64 64 896 896" data-icon="search" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path></svg></i></button></span></span><span class="ant-select-search__field__mirror"> </span></div>
|
||||
<div class="ant-select-search__field__wrap"><span class="ant-input-affix-wrapper ant-select-search__field"><input placeholder="input here" type="text" value="" class="ant-input"><span class="ant-input-suffix"><button type="button" class="search-btn ant-btn ant-btn-primary ant-btn-lg" style="margin-right: -12px;"><i aria-label="icon: search" class="anticon anticon-search"><svg viewBox="64 64 896 896" data-icon="search" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0 0 11.6 0l43.6-43.5a8.2 8.2 0 0 0 0-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z"></path></svg></i></button></span></span><span class="ant-select-search__field__mirror"> </span></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div><span unselectable="on" class="ant-select-arrow" style="user-select: none;"><i aria-label="icon: down" class="ant-select-arrow-icon anticon anticon-down"><svg viewBox="64 64 896 896" data-icon="down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path></svg></i></span>
|
||||
|
@ -1,38 +0,0 @@
|
||||
<cn>
|
||||
#### 基本使用
|
||||
基本使用。通过 dataSource 设置自动完成的数据源
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Basic Usage
|
||||
Basic Usage, set datasource of autocomplete with `dataSource` property.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<a-auto-complete
|
||||
:dataSource="dataSource"
|
||||
style="width: 200px"
|
||||
@select="onSelect"
|
||||
@search="handleSearch"
|
||||
placeholder="input here"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dataSource: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleSearch(value) {
|
||||
this.dataSource = !value ? [] : [value, value + value, value + value + value];
|
||||
},
|
||||
onSelect(value) {
|
||||
console.log('onSelect', value);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
@ -1,145 +0,0 @@
|
||||
<cn>
|
||||
#### 查询模式 - 确定类目
|
||||
查询模式 - 确定类目
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Lookup-Patterns - Certain Category
|
||||
Lookup-Patterns - Certain Category
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div class="certain-category-search-wrapper" style="width: 250px">
|
||||
<a-auto-complete
|
||||
class="certain-category-search"
|
||||
dropdownClassName="certain-category-search-dropdown"
|
||||
:dropdownMatchSelectWidth="false"
|
||||
:dropdownStyle="{width: '300px'}"
|
||||
size="large"
|
||||
style="width: 100%"
|
||||
placeholder="input here"
|
||||
optionLabelProp="value"
|
||||
>
|
||||
<template slot="dataSource">
|
||||
<a-select-opt-group v-for="group in dataSource" :key="group.title">
|
||||
<span slot="label">
|
||||
{{group.title}}
|
||||
<a
|
||||
style="float: right"
|
||||
href="https://www.google.com/search?q=antd"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>更多
|
||||
</a>
|
||||
</span>
|
||||
<a-select-option v-for="opt in group.children" :key="opt.title" :value="opt.title">
|
||||
{{opt.title}}
|
||||
<span class="certain-search-item-count">{{opt.count}} 人 关注</span>
|
||||
</a-select-option>
|
||||
</a-select-opt-group>
|
||||
<a-select-option disabled key="all" class="show-all">
|
||||
<a href="https://www.google.com/search?q=antd" target="_blank" rel="noopener noreferrer">
|
||||
查看所有结果
|
||||
</a>
|
||||
</a-select-option>
|
||||
</template>
|
||||
<a-input>
|
||||
<a-icon slot="suffix" type="search" class="certain-category-icon" />
|
||||
</a-input>
|
||||
</a-auto-complete>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const dataSource = [
|
||||
{
|
||||
title: '话题',
|
||||
children: [
|
||||
{
|
||||
title: 'AntDesign',
|
||||
count: 10000,
|
||||
},
|
||||
{
|
||||
title: 'AntDesign UI',
|
||||
count: 10600,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '问题',
|
||||
children: [
|
||||
{
|
||||
title: 'AntDesign UI 有多好',
|
||||
count: 60100,
|
||||
},
|
||||
{
|
||||
title: 'AntDesign 是啥',
|
||||
count: 30010,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '文章',
|
||||
children: [
|
||||
{
|
||||
title: 'AntDesign 是一个设计语言',
|
||||
count: 100000,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dataSource,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.certain-category-search-dropdown .ant-select-dropdown-menu-item-group-title {
|
||||
color: #666;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.certain-category-search-dropdown .ant-select-dropdown-menu-item-group {
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
}
|
||||
|
||||
.certain-category-search-dropdown .ant-select-dropdown-menu-item {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.certain-category-search-dropdown .ant-select-dropdown-menu-item.show-all {
|
||||
text-align: center;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.certain-category-search-dropdown .ant-select-dropdown-menu {
|
||||
max-height: 300px;
|
||||
}
|
||||
</style>
|
||||
<style scoped>
|
||||
.certain-category-search-wrapper
|
||||
>>> .certain-category-search.ant-select-auto-complete
|
||||
.ant-input-affix-wrapper
|
||||
.ant-input-suffix {
|
||||
right: 12px;
|
||||
}
|
||||
.certain-category-search-wrapper >>> .certain-search-item-count {
|
||||
position: absolute;
|
||||
color: #999;
|
||||
right: 16px;
|
||||
}
|
||||
.certain-category-search-wrapper
|
||||
>>> .certain-category-search.ant-select-focused
|
||||
.certain-category-icon {
|
||||
color: #108ee9;
|
||||
}
|
||||
.certain-category-search-wrapper >>> .certain-category-icon {
|
||||
color: #6e6e6e;
|
||||
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
```
|
@ -1,47 +0,0 @@
|
||||
<cn>
|
||||
#### 自定义输入组件
|
||||
自定义输入组件。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Customize Input Component
|
||||
Customize Input Component
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<a-auto-complete
|
||||
:dataSource="dataSource"
|
||||
style="width: 200px"
|
||||
@search="handleSearch"
|
||||
@select="onSelect"
|
||||
>
|
||||
<a-textarea
|
||||
placeholder="input here"
|
||||
class="custom"
|
||||
style="height: 50px"
|
||||
@keypress="handleKeyPress"
|
||||
/>
|
||||
</a-auto-complete>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dataSource: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onSelect(value) {
|
||||
console.log('onSelect', value);
|
||||
},
|
||||
handleSearch(value) {
|
||||
this.dataSource = !value ? [] : [value, value + value, value + value + value];
|
||||
},
|
||||
handleKeyPress(ev) {
|
||||
console.log('handleKeyPress', ev);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
@ -1,49 +0,0 @@
|
||||
<script>
|
||||
import Basic from './basic';
|
||||
import CertainCategory from './certain-category';
|
||||
import Custom from './custom';
|
||||
import NonCaseSensitive from './non-case-sensitive';
|
||||
import Options from './options';
|
||||
import UncertainCategory from './uncertain-category';
|
||||
|
||||
import CN from '../index.zh-CN.md';
|
||||
import US from '../index.en-US.md';
|
||||
const md = {
|
||||
cn: `# AutoComplete 自动完成
|
||||
输入框自动完成功能。
|
||||
## 何时使用
|
||||
需要自动完成时。
|
||||
## 代码演示`,
|
||||
us: `# AutoComplete
|
||||
Autocomplete function of input field.
|
||||
## When To Use
|
||||
When there is a need for autocomplete functionality.
|
||||
## Examples
|
||||
`,
|
||||
};
|
||||
export default {
|
||||
category: 'Components',
|
||||
subtitle: '自动完成',
|
||||
type: 'Data Entry',
|
||||
zhType: '数据录入',
|
||||
cols: 2,
|
||||
title: 'AutoComplete',
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<md cn={md.cn} us={md.us} />
|
||||
<Basic />
|
||||
<CertainCategory />
|
||||
<Custom />
|
||||
<NonCaseSensitive />
|
||||
<Options />
|
||||
<UncertainCategory />
|
||||
<api>
|
||||
<CN slot="cn" />
|
||||
<US />
|
||||
</api>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
</script>
|
@ -1,36 +0,0 @@
|
||||
<cn>
|
||||
#### 不区分大小写
|
||||
不区分大小写的 AutoComplete
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Non-case-sensitive AutoComplete
|
||||
A non-case-sensitive AutoComplete
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<a-auto-complete
|
||||
:dataSource="dataSource"
|
||||
style="width: 200px"
|
||||
placeholder="input here"
|
||||
:filterOption="filterOption"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dataSource: ['Burns Bay Road', 'Downing Street', 'Wall Street'],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
filterOption(input, option) {
|
||||
return (
|
||||
option.componentOptions.children[0].text.toUpperCase().indexOf(input.toUpperCase()) >= 0
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
@ -1,39 +0,0 @@
|
||||
<cn>
|
||||
#### 自定义选项
|
||||
也可以直接传递slot="dataSource"的Option
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Customized
|
||||
You could pass `slot="dataSource` as children of `AutoComplete`, instead of using `dataSource`。
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<a-auto-complete style="width: 200px" @search="handleSearch" placeholder="input here">
|
||||
<template slot="dataSource">
|
||||
<a-select-option v-for="email in result" :key="email">{{email}}</a-select-option>
|
||||
</template>
|
||||
</a-auto-complete>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
result: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleSearch(value) {
|
||||
let result;
|
||||
if (!value || value.indexOf('@') >= 0) {
|
||||
result = [];
|
||||
} else {
|
||||
result = ['gmail.com', '163.com', 'qq.com'].map(domain => `${value}@${domain}`);
|
||||
}
|
||||
this.result = result;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
@ -1,110 +0,0 @@
|
||||
<cn>
|
||||
#### 查询模式 - 不确定类目
|
||||
查询模式 - 不确定类目
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Lookup-Patterns - Uncertain Category
|
||||
Lookup-Patterns - Uncertain Category
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div class="global-search-wrapper" style="width: 300px">
|
||||
<a-auto-complete
|
||||
class="global-search"
|
||||
size="large"
|
||||
style="width: 100%"
|
||||
@select="onSelect"
|
||||
@search="handleSearch"
|
||||
placeholder="input here"
|
||||
optionLabelProp="text"
|
||||
>
|
||||
<template slot="dataSource">
|
||||
<a-select-option v-for="item in dataSource" :key="item.category" :text="item.category">
|
||||
{{item.query}} 在
|
||||
<a
|
||||
:href="`https://s.taobao.com/search?q=${item.query}`"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{{item.category}}
|
||||
</a>
|
||||
区块中
|
||||
<span className="global-search-item-count">约 {{item.count}} 个结果</span>
|
||||
</a-select-option>
|
||||
</template>
|
||||
<a-input>
|
||||
<a-button slot="suffix" class="search-btn" size="large" type="primary">
|
||||
<a-icon type="search" />
|
||||
</a-button>
|
||||
</a-input>
|
||||
</a-auto-complete>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dataSource: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onSelect(value) {
|
||||
console.log('onSelect', value);
|
||||
},
|
||||
|
||||
handleSearch(value) {
|
||||
this.dataSource = value ? this.searchResult(value) : [];
|
||||
},
|
||||
|
||||
getRandomInt(max, min = 0) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
},
|
||||
|
||||
searchResult(query) {
|
||||
return new Array(this.getRandomInt(5))
|
||||
.join('.')
|
||||
.split('.')
|
||||
.map((item, idx) => ({
|
||||
query,
|
||||
category: `${query}${idx}`,
|
||||
count: this.getRandomInt(200, 100),
|
||||
}));
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.global-search-wrapper {
|
||||
padding-right: 50px;
|
||||
}
|
||||
|
||||
.global-search {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.global-search.ant-select-auto-complete .ant-select-selection--single {
|
||||
margin-right: -46px;
|
||||
}
|
||||
|
||||
.global-search.ant-select-auto-complete .ant-input-affix-wrapper .ant-input:not(:last-child) {
|
||||
padding-right: 62px;
|
||||
}
|
||||
|
||||
.global-search.ant-select-auto-complete .ant-input-affix-wrapper .ant-input-suffix {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.global-search.ant-select-auto-complete .ant-input-affix-wrapper .ant-input-suffix button {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.global-search-item-count {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
}
|
||||
</style>
|
||||
```
|
@ -1,40 +0,0 @@
|
||||
## API
|
||||
|
||||
```html
|
||||
<a-auto-complete :dataSource="dataSource" />
|
||||
```
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| allowClear | Show clear button, effective in multiple mode only. | boolean | false |
|
||||
| autoFocus | get focus when component mounted | boolean | false |
|
||||
| backfill | backfill selected item the input when using keyboard | boolean | false |
|
||||
| slot="default" (for customize input element) | customize input element | HTMLInputElement / HTMLTextAreaElement | `<Input />` |
|
||||
| dataSource | Data source for autocomplete | slot \| [DataSourceItemType](https://github.com/vueComponent/ant-design-vue/blob/724d53b907e577cf5880c1e6742d4c3f924f8f49/components/auto-complete/index.vue#L9)\[] | |
|
||||
| defaultActiveFirstOption | Whether active first option by default | boolean | true |
|
||||
| defaultValue | Initial selected option. | string\|string\[]\| - |
|
||||
| disabled | Whether disabled select | boolean | false |
|
||||
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns `true`, the option will be included in the filtered set; Otherwise, it will be excluded. | boolean or function(inputValue, option) | true |
|
||||
| optionLabelProp | Which prop value of option will render as content of select. | string | `children` |
|
||||
| placeholder | placeholder of input | string | - |
|
||||
| value(v-model) | selected option | string\|string\[]\|{ key: string, label: string\|vNodes }\|Array<{ key: string, label: string\|vNodes }> | - |
|
||||
| defaultOpen | Initial open state of dropdown | boolean | - |
|
||||
| open | Controlled open state of dropdown | boolean | - |
|
||||
|
||||
### events
|
||||
|
||||
| Events Name | Description | Arguments |
|
||||
| --- | --- | --- |
|
||||
| change | Called when select an option or input value change, or value of input is changed | function(value) |
|
||||
| blur | Called when leaving the component. | function() |
|
||||
| focus | Called when entering the component | function() |
|
||||
| search | Called when searching items. | function(value) | - |
|
||||
| select | Called when a option is selected. param is option's value and option instance. | function(value, option) |
|
||||
| dropdownVisibleChange | Call when dropdown open | function(open) |
|
||||
|
||||
## Methods
|
||||
|
||||
| Name | Description |
|
||||
| ------- | ------------ |
|
||||
| blur() | remove focus |
|
||||
| focus() | get focus |
|
@ -32,6 +32,7 @@ const AutoCompleteProps = {
|
||||
value: SelectValue,
|
||||
defaultValue: SelectValue,
|
||||
dataSource: PropTypes.array,
|
||||
dropdownMenuStyle: PropTypes.object,
|
||||
optionLabelProp: String,
|
||||
dropdownMatchSelectWidth: PropTypes.bool,
|
||||
// onChange?: (value: SelectValue) => void;
|
||||
|
@ -1,40 +0,0 @@
|
||||
## API
|
||||
|
||||
```html
|
||||
<a-auto-complete :dataSource="dataSource" />
|
||||
```
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| allowClear | 支持清除, 单选模式有效 | boolean | false |
|
||||
| autoFocus | 自动获取焦点 | boolean | false |
|
||||
| backfill | 使用键盘选择选项的时候把选中项回填到输入框中 | boolean | false |
|
||||
| slot="default" (自定义输入框) | 自定义输入框 | HTMLInputElement / HTMLTextAreaElement | `<Input />` |
|
||||
| dataSource | 自动完成的数据源 | slot \| [DataSourceItemType](https://github.com/vueComponent/ant-design-vue/blob/724d53b907e577cf5880c1e6742d4c3f924f8f49/components/auto-complete/index.vue#L9)\[] | |
|
||||
| defaultActiveFirstOption | 是否默认高亮第一个选项。 | boolean | true |
|
||||
| defaultValue | 指定默认选中的条目 | string\|string\[]\| 无 |
|
||||
| disabled | 是否禁用 | boolean | false |
|
||||
| filterOption | 是否根据输入项进行筛选。当其为一个函数时,会接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 `true`,反之则返回 `false`。 | boolean or function(inputValue, option) | true |
|
||||
| optionLabelProp | 回填到选择框的 Option 的属性值,默认是 Option 的子元素。比如在子元素需要高亮效果时,此值可以设为 `value`。 | string | `children` |
|
||||
| placeholder | 输入框提示 | string \| slot | - |
|
||||
| value(v-model) | 指定当前选中的条目 | string\|string\[]\|{ key: string, label: string\|vNodes }\|Array<{ key: string, label: string\|vNodes }> | 无 |
|
||||
| defaultOpen | 是否默认展开下拉菜单 | boolean | - |
|
||||
| open | 是否展开下拉菜单 | boolean | - |
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
| --- | --- | --- |
|
||||
| change | 选中 option,或 input 的 value 变化时,调用此函数 | function(value) |
|
||||
| blur | 失去焦点时的回调 | function() |
|
||||
| focus | 获得焦点时的回调 | function() |
|
||||
| search | 搜索补全项的时候调用 | function(value) |
|
||||
| select | 被选中时调用,参数为选中项的 value 值 | function(value, option) |
|
||||
| dropdownVisibleChange | 展开下拉菜单的回调 | function(open) |
|
||||
|
||||
## 方法
|
||||
|
||||
| 名称 | 描述 |
|
||||
| ------- | -------- |
|
||||
| blur() | 移除焦点 |
|
||||
| focus() | 获取焦点 |
|
@ -1,4 +1,6 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
|
||||
// style dependencies
|
||||
import '../../select/style';
|
||||
import '../../input/style';
|
||||
|
@ -1,4 +1,4 @@
|
||||
@import '../../style/themes/default';
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import '../../input/style/mixin';
|
||||
|
||||
@ -54,6 +54,7 @@
|
||||
}
|
||||
&[disabled] {
|
||||
.disabled;
|
||||
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
@ -81,3 +82,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/14156
|
||||
.@{input-prefix-cls}-group > .@{autocomplete-prefix-cls} {
|
||||
.@{select-prefix-cls}-search__field.@{input-prefix-cls}-affix-wrapper {
|
||||
display: inline;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { ConfigConsumerProps } from '../config-provider';
|
||||
import Icon from '../icon';
|
||||
import { getListeners } from '../_util/props-util';
|
||||
import { getListeners, getComponentFromProp } from '../_util/props-util';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
|
||||
export default {
|
||||
name: 'AAvatar',
|
||||
@ -22,7 +23,7 @@ export default {
|
||||
src: String,
|
||||
/** Srcset of image avatar */
|
||||
srcSet: String,
|
||||
icon: String,
|
||||
icon: PropTypes.any,
|
||||
alt: String,
|
||||
loadError: Function,
|
||||
},
|
||||
@ -32,6 +33,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
isImgExist: true,
|
||||
isMounted: false,
|
||||
scale: 1,
|
||||
};
|
||||
},
|
||||
@ -46,38 +48,35 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.prevChildren = this.$slots.default;
|
||||
this.prevState = { ...this.$data };
|
||||
this.$nextTick(() => {
|
||||
this.setScale();
|
||||
this.isMounted = true;
|
||||
});
|
||||
},
|
||||
updated() {
|
||||
this.$nextTick(() => {
|
||||
this.setScale();
|
||||
});
|
||||
},
|
||||
updated() {
|
||||
if (
|
||||
this.preChildren !== this.$slots.default ||
|
||||
(this.prevState.scale !== this.$data.scale && this.$data.scale === 1) ||
|
||||
this.prevState.isImgExist !== this.$data.isImgExist
|
||||
) {
|
||||
this.$nextTick(() => {
|
||||
this.setScale();
|
||||
});
|
||||
}
|
||||
this.preChildren = this.$slots.default;
|
||||
this.prevState = { ...this.$data };
|
||||
},
|
||||
methods: {
|
||||
setScale() {
|
||||
const childrenNode = this.$refs.avatarChildren;
|
||||
if (childrenNode) {
|
||||
const childrenWidth = childrenNode.offsetWidth;
|
||||
const avatarWidth = this.$el.getBoundingClientRect().width;
|
||||
if (avatarWidth - 8 < childrenWidth) {
|
||||
this.scale = (avatarWidth - 8) / childrenWidth;
|
||||
} else {
|
||||
this.scale = 1;
|
||||
}
|
||||
this.$forceUpdate();
|
||||
if (!this.$refs.avatarChildren || !this.$refs.avatarNode) {
|
||||
return;
|
||||
}
|
||||
const childrenWidth = this.$refs.avatarChildren.offsetWidth; // offsetWidth avoid affecting be transform scale
|
||||
const nodeWidth = this.$refs.avatarNode.offsetWidth;
|
||||
// denominator is 0 is no meaning
|
||||
if (
|
||||
childrenWidth === 0 ||
|
||||
nodeWidth === 0 ||
|
||||
(this.lastChildrenWidth === childrenWidth && this.lastNodeWidth === nodeWidth)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.lastChildrenWidth = childrenWidth;
|
||||
this.lastNodeWidth = nodeWidth;
|
||||
// add 4px gap for each side to get better performance
|
||||
this.scale = nodeWidth - 8 < childrenWidth ? (nodeWidth - 8) / childrenWidth : 1;
|
||||
},
|
||||
handleImgLoadError() {
|
||||
const { loadError } = this.$props;
|
||||
@ -88,12 +87,12 @@ export default {
|
||||
},
|
||||
},
|
||||
render() {
|
||||
const { prefixCls: customizePrefixCls, shape, size, src, icon, alt, srcSet } = this.$props;
|
||||
|
||||
const { prefixCls: customizePrefixCls, shape, size, src, alt, srcSet } = this.$props;
|
||||
const icon = getComponentFromProp(this, 'icon');
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('avatar', customizePrefixCls);
|
||||
|
||||
const { isImgExist, scale } = this.$data;
|
||||
const { isImgExist, scale, isMounted } = this.$data;
|
||||
|
||||
const sizeCls = {
|
||||
[`${prefixCls}-lg`]: size === 'large',
|
||||
@ -122,10 +121,14 @@ export default {
|
||||
if (src && isImgExist) {
|
||||
children = <img src={src} srcSet={srcSet} onError={this.handleImgLoadError} alt={alt} />;
|
||||
} else if (icon) {
|
||||
children = <Icon type={icon} />;
|
||||
if (typeof icon === 'string') {
|
||||
children = <Icon type={icon} />;
|
||||
} else {
|
||||
children = icon;
|
||||
}
|
||||
} else {
|
||||
const childrenNode = this.$refs.avatarChildren;
|
||||
if (childrenNode || (scale !== 1 && childrenNode)) {
|
||||
if (childrenNode || scale !== 1) {
|
||||
const transformString = `scale(${scale}) translateX(-50%)`;
|
||||
const childrenStyle = {
|
||||
msTransform: transformString,
|
||||
@ -148,15 +151,21 @@ export default {
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
const childrenStyle = {};
|
||||
if (!isMounted) {
|
||||
childrenStyle.opacity = 0;
|
||||
}
|
||||
children = (
|
||||
<span class={`${prefixCls}-string`} ref="avatarChildren">
|
||||
<span class={`${prefixCls}-string`} ref="avatarChildren" style={{ opacity: 0 }}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<span {...{ on: getListeners(this), class: classString, style: sizeStyle }}>{children}</span>
|
||||
<span ref="avatarNode" {...{ on: getListeners(this), class: classString, style: sizeStyle }}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
@ -3,6 +3,26 @@ import { asyncExpect } from '@/tests/utils';
|
||||
import Avatar from '..';
|
||||
|
||||
describe('Avatar Render', () => {
|
||||
let originOffsetWidth;
|
||||
beforeAll(() => {
|
||||
// Mock offsetHeight
|
||||
originOffsetWidth = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetWidth').get;
|
||||
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
|
||||
get() {
|
||||
if (this.className === 'ant-avatar-string') {
|
||||
return 100;
|
||||
}
|
||||
return 80;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
// Restore Mock offsetHeight
|
||||
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
|
||||
get: originOffsetWidth,
|
||||
});
|
||||
});
|
||||
it('Render long string correctly', () => {
|
||||
const wrapper = mount(Avatar, {
|
||||
slots: {
|
||||
@ -25,6 +45,9 @@ describe('Avatar Render', () => {
|
||||
attachToDocument: true,
|
||||
});
|
||||
wrapper.vm.setScale = jest.fn(() => {
|
||||
if (wrapper.vm.scale === 0.5) {
|
||||
return;
|
||||
}
|
||||
wrapper.setData({ scale: 0.5 });
|
||||
wrapper.vm.$forceUpdate();
|
||||
});
|
||||
@ -35,14 +58,14 @@ describe('Avatar Render', () => {
|
||||
const children = wrapper.findAll('.ant-avatar-string');
|
||||
expect(children.length).toBe(1);
|
||||
expect(children.at(0).text()).toBe('Fallback');
|
||||
expect(wrapper.vm.setScale).toBeCalled();
|
||||
expect(wrapper.vm.setScale).toHaveBeenCalled();
|
||||
});
|
||||
await asyncExpect(() => {
|
||||
expect(global.document.body.querySelector('.ant-avatar-string').style.transform).toContain(
|
||||
'scale(0.5)',
|
||||
);
|
||||
global.document.body.innerHTML = '';
|
||||
}, 0);
|
||||
}, 1000);
|
||||
});
|
||||
it('should handle onError correctly', async () => {
|
||||
global.document.body.innerHTML = '';
|
||||
|
@ -1,14 +1,14 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/avatar/demo/badge.md correctly 1`] = `<div><span style="margin-right: 24px;"><span class="ant-badge"><span class="ant-avatar ant-avatar-square ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span><sup title="1" class="ant-scroll-number ant-badge-count ant-badge-zoom-enter" data-show="true"><span class="ant-scroll-number-only" style="transition: none; transform: translateY(-1100%);"><p class="">0</p><p class="">1</p><p class="">2</p><p class="">3</p><p class="">4</p><p class="">5</p><p class="">6</p><p class="">7</p><p class="">8</p><p class="">9</p><p class="">0</p><p class="current">1</p><p class="">2</p><p class="">3</p><p class="">4</p><p class="">5</p><p class="">6</p><p class="">7</p><p class="">8</p><p class="">9</p><p class="">0</p><p class="">1</p><p class="">2</p><p class="">3</p><p class="">4</p><p class="">5</p><p class="">6</p><p class="">7</p><p class="">8</p><p class="">9</p></span></sup></span></span> <span><span class="ant-badge"><span class="ant-avatar ant-avatar-square ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span><sup class="ant-scroll-number ant-badge-dot ant-badge-zoom-enter" data-show="true"></sup></span></span></div>`;
|
||||
exports[`renders ./antdv-demo/avatar/demo/badge.md correctly 1`] = `<div><span style="margin-right: 24px;"><span class="ant-badge"><span class="ant-avatar ant-avatar-square ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span><sup title="1" class="ant-scroll-number ant-badge-count ant-badge-zoom-enter" data-show="true"><span class="ant-scroll-number-only" style="transition: none; transform: translateY(-1100%);"><p class="ant-scroll-number-only-unit">0</p><p class="ant-scroll-number-only-unit">1</p><p class="ant-scroll-number-only-unit">2</p><p class="ant-scroll-number-only-unit">3</p><p class="ant-scroll-number-only-unit">4</p><p class="ant-scroll-number-only-unit">5</p><p class="ant-scroll-number-only-unit">6</p><p class="ant-scroll-number-only-unit">7</p><p class="ant-scroll-number-only-unit">8</p><p class="ant-scroll-number-only-unit">9</p><p class="ant-scroll-number-only-unit">0</p><p class="ant-scroll-number-only-unit current">1</p><p class="ant-scroll-number-only-unit">2</p><p class="ant-scroll-number-only-unit">3</p><p class="ant-scroll-number-only-unit">4</p><p class="ant-scroll-number-only-unit">5</p><p class="ant-scroll-number-only-unit">6</p><p class="ant-scroll-number-only-unit">7</p><p class="ant-scroll-number-only-unit">8</p><p class="ant-scroll-number-only-unit">9</p><p class="ant-scroll-number-only-unit">0</p><p class="ant-scroll-number-only-unit">1</p><p class="ant-scroll-number-only-unit">2</p><p class="ant-scroll-number-only-unit">3</p><p class="ant-scroll-number-only-unit">4</p><p class="ant-scroll-number-only-unit">5</p><p class="ant-scroll-number-only-unit">6</p><p class="ant-scroll-number-only-unit">7</p><p class="ant-scroll-number-only-unit">8</p><p class="ant-scroll-number-only-unit">9</p></span></sup></span></span> <span><span class="ant-badge"><span class="ant-avatar ant-avatar-square ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span><sup class="ant-scroll-number ant-badge-dot ant-badge-zoom-enter" data-show="true"></sup></span></span></div>`;
|
||||
|
||||
exports[`renders ./components/avatar/demo/basic.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/avatar/demo/basic.md correctly 1`] = `
|
||||
<div>
|
||||
<div><span class="ant-avatar ant-avatar-circle ant-avatar-icon" style="width: 64px; height: 64px; line-height: 64px; font-size: 32px;"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-lg ant-avatar-circle ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-circle ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-sm ant-avatar-circle ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span></div> <br>
|
||||
<div><span class="ant-avatar ant-avatar-square ant-avatar-icon" style="width: 64px; height: 64px; line-height: 64px; font-size: 32px;"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-lg ant-avatar-square ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-square ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-sm ant-avatar-square ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span></div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/avatar/demo/dynamic.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-lg ant-avatar-square" style="background-color: rgb(245, 106, 0); vertical-align: middle;"><span class="ant-avatar-string">U</span></span> <button type="button" class="ant-btn ant-btn-sm" style="vertical-align: middle;"><span>改 变</span></button></div>`;
|
||||
exports[`renders ./antdv-demo/avatar/demo/dynamic.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-lg ant-avatar-square" style="background-color: rgb(245, 106, 0); vertical-align: middle;"><span class="ant-avatar-string" style="opacity: 0;">U</span></span> <button type="button" class="ant-btn ant-btn-sm" style="vertical-align: middle;"><span>改 变</span></button></div>`;
|
||||
|
||||
exports[`renders ./components/avatar/demo/type.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-circle ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string">U</span></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string">USER</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span> <span class="ant-avatar ant-avatar-circle" style="color: rgb(245, 106, 0); background-color: rgb(253, 227, 207);"><span class="ant-avatar-string">U</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-icon" style="background-color: rgb(135, 208, 104);"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span></div>`;
|
||||
exports[`renders ./antdv-demo/avatar/demo/type.md correctly 1`] = `<div><span class="ant-avatar ant-avatar-circle ant-avatar-icon"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-circle ant-avatar-icon"><i slot="icon" aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string" style="opacity: 0;">U</span></span> <span class="ant-avatar ant-avatar-circle"><span class="ant-avatar-string" style="opacity: 0;">USER</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-image"><img src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"></span> <span class="ant-avatar ant-avatar-circle" style="color: rgb(245, 106, 0); background-color: rgb(253, 227, 207);"><span class="ant-avatar-string" style="opacity: 0;">U</span></span> <span class="ant-avatar ant-avatar-circle ant-avatar-icon" style="background-color: rgb(135, 208, 104);"><i aria-label="icon: user" class="anticon anticon-user"><svg viewBox="64 64 896 896" data-icon="user" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M858.5 763.6a374 374 0 0 0-80.6-119.5 375.63 375.63 0 0 0-119.5-80.6c-.4-.2-.8-.3-1.2-.5C719.5 518 760 444.7 760 362c0-137-111-248-248-248S264 225 264 362c0 82.7 40.5 156 102.8 201.1-.4.2-.8.3-1.2.5-44.8 18.9-85 46-119.5 80.6a375.63 375.63 0 0 0-80.6 119.5A371.7 371.7 0 0 0 136 901.8a8 8 0 0 0 8 8.2h60c4.4 0 7.9-3.5 8-7.8 2-77.2 33-149.5 87.8-204.3 56.7-56.7 132-87.9 212.2-87.9s155.5 31.2 212.2 87.9C779 752.7 810 825 812 902.2c.1 4.4 3.6 7.8 8 7.8h60a8 8 0 0 0 8-8.2c-1-47.8-10.9-94.3-29.5-138.2zM512 534c-45.9 0-89.1-17.9-121.6-50.4S340 407.9 340 362c0-45.9 17.9-89.1 50.4-121.6S466.1 190 512 190s89.1 17.9 121.6 50.4S684 316.1 684 362c0 45.9-17.9 89.1-50.4 121.6S557.9 534 512 534z"></path></svg></i></span></div>`;
|
||||
|
@ -1,22 +0,0 @@
|
||||
<cn>
|
||||
#### 带徽标的头像
|
||||
通常用于消息提示。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### With Badge
|
||||
Usually used for messages remind.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<span style="margin-right:24px">
|
||||
<a-badge :count="1"><a-avatar shape="square" icon="user"/></a-badge>
|
||||
</span>
|
||||
<span>
|
||||
<a-badge dot><a-avatar shape="square" icon="user"/></a-badge>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
```
|
@ -1,29 +0,0 @@
|
||||
<cn>
|
||||
#### 基本
|
||||
头像有三种尺寸,两种形状可选。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### basic
|
||||
Three sizes and two shapes are available.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<a-avatar :size="64" icon="user" />
|
||||
<a-avatar size="large" icon="user" />
|
||||
<a-avatar icon="user" />
|
||||
<a-avatar size="small" icon="user" />
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
<a-avatar shape="square" :size="64" icon="user" />
|
||||
<a-avatar shape="square" size="large" icon="user" />
|
||||
<a-avatar shape="square" icon="user" />
|
||||
<a-avatar shape="square" size="small" icon="user" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
@ -1,41 +0,0 @@
|
||||
<cn>
|
||||
#### 自动调整字符大小
|
||||
对于字符型的头像,当字符串较长时,字体大小可以根据头像宽度自动调整。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Autoset Font Size
|
||||
For letter type Avatar, when the letters are too long to display, the font size can be automatically adjusted according to the width of the Avatar.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-avatar shape="square" size="large" :style="{backgroundColor: color, verticalAlign: 'middle'}"
|
||||
>{{avatarValue}}</a-avatar
|
||||
>
|
||||
<a-button size="small" :style="{ marginLeft: 16, verticalAlign: 'middle' }" @click="changeValue"
|
||||
>改变</a-button
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const UserList = ['U', 'Lucy', 'Tom', 'Edward'];
|
||||
const colorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae'];
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
avatarValue: UserList[0],
|
||||
color: colorList[0],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
changeValue() {
|
||||
const index = UserList.indexOf(this.avatarValue);
|
||||
this.avatarValue = index < UserList.length - 1 ? UserList[index + 1] : UserList[0];
|
||||
this.color = index < colorList.length - 1 ? colorList[index + 1] : colorList[0];
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
```
|
@ -1,49 +0,0 @@
|
||||
<script>
|
||||
import Basic from './basic';
|
||||
import Badge from './badge';
|
||||
import Type from './type';
|
||||
import Dynamic from './dynamic';
|
||||
import CN from '../index.zh-CN.md';
|
||||
import US from '../index.en-US.md';
|
||||
|
||||
const md = {
|
||||
cn: `# Avatar头像
|
||||
用来代表用户或事物,支持图片、图标或字符展示。
|
||||
## 设计师专属
|
||||
安装 [Kitchen Sketch 插件 <EFBFBD>](https://kitchen.alipay.com),一键填充高逼格头像和文本.
|
||||
|
||||
## 代码演示`,
|
||||
us: `# Avatar
|
||||
Avatars can be used to represent people or objects. It supports images, 'Icon's, or letters.
|
||||
## Examples
|
||||
`,
|
||||
};
|
||||
export default {
|
||||
category: 'Components',
|
||||
subtitle: '头像',
|
||||
type: 'Data Display',
|
||||
zhType: '数据展示',
|
||||
title: 'Avatar',
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<md cn={md.cn} us={md.us} />
|
||||
<Basic />
|
||||
<br />
|
||||
<Badge />
|
||||
<br />
|
||||
<Type />
|
||||
<br />
|
||||
<Dynamic />
|
||||
<br />
|
||||
<api>
|
||||
<template slot="cn">
|
||||
<CN />
|
||||
</template>
|
||||
<US />
|
||||
</api>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
</script>
|
@ -1,22 +0,0 @@
|
||||
<cn>
|
||||
#### 类型
|
||||
支持三种类型:图片、Icon 以及字符,其中 Icon 和字符型可以自定义图标颜色及背景色。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Type
|
||||
Image, Icon and letter are supported, and the latter two kinds avatar can have custom colors and background colors.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-avatar icon="user" />
|
||||
<a-avatar>U</a-avatar>
|
||||
<a-avatar>USER</a-avatar>
|
||||
<a-avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
|
||||
<a-avatar style="color: #f56a00; backgroundColor: #fde3cf">U</a-avatar>
|
||||
<a-avatar style="backgroundColor:#87d068" icon="user" />
|
||||
</div>
|
||||
</template>
|
||||
```
|
@ -1,11 +0,0 @@
|
||||
## API
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| icon | the `Icon` type for an icon avatar, see `Icon` Component | string | - |
|
||||
| shape | the shape of avatar | `circle` \| `square` | `circle` |
|
||||
| size | the size of the avatar | number \| string: `large` `small` `default` | `default` |
|
||||
| src | the address of the image for an image avatar | string | - |
|
||||
| srcSet | a list of sources to use for different screen resolutions | string | - |
|
||||
| alt | This attribute defines the alternative text describing the image | string | - |
|
||||
| loadError | handler when img load error, return false to prevent default fallback behavior | () => boolean | - |
|
@ -1,11 +0,0 @@
|
||||
## API
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| icon | 设置头像的图标类型,参考 `Icon` 组件 | string | - |
|
||||
| shape | 指定头像的形状 | Enum{ 'circle', 'square' } | `circle` |
|
||||
| size | 设置头像的大小 | number \| Enum{ 'large', 'small', 'default' } | `default` |
|
||||
| src | 图片类头像的资源地址 | string | - |
|
||||
| srcSet | 设置图片类头像响应式资源地址 | string | - |
|
||||
| alt | 图像无法显示时的替代文本 | string | - |
|
||||
| loadError | 图片加载失败的事件,返回 false 会关闭组件默认的 fallback 行为 | () => boolean | - |
|
@ -1,10 +1,11 @@
|
||||
@import '../../style/themes/default';
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
|
||||
@avatar-prefix-cls: ~'@{ant-prefix}-avatar';
|
||||
|
||||
.@{avatar-prefix-cls} {
|
||||
.reset-component;
|
||||
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
@ -36,6 +37,7 @@
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders ./components/back-top/demo/basic.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/back-top/demo/basic.md correctly 1`] = `
|
||||
<div>
|
||||
<!---->
|
||||
Scroll down to see the bottom-right
|
||||
@ -9,7 +9,7 @@ exports[`renders ./components/back-top/demo/basic.md correctly 1`] = `
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/back-top/demo/custom.md correctly 1`] = `
|
||||
exports[`renders ./antdv-demo/back-top/demo/custom.md correctly 1`] = `
|
||||
<div id="components-back-top-demo-custom">
|
||||
<!---->
|
||||
Scroll down to see the bottom-right
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { mount } from '@vue/test-utils';
|
||||
import BackTop from '..';
|
||||
import { sleep } from '../../../tests/utils';
|
||||
|
||||
describe('BackTop', () => {
|
||||
it('should scroll to top after click it', async () => {
|
||||
@ -8,12 +9,39 @@ describe('BackTop', () => {
|
||||
visibilityHeight: -1,
|
||||
},
|
||||
});
|
||||
document.documentElement.scrollTop = 400;
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
|
||||
window.scrollY = y;
|
||||
window.pageYOffset = y;
|
||||
});
|
||||
window.scrollTo(0, 400);
|
||||
// trigger scroll manually
|
||||
wrapper.vm.handleScroll();
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
await sleep();
|
||||
wrapper.find('.ant-back-top').trigger('click');
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
expect(Math.abs(Math.round(document.documentElement.scrollTop))).toBe(0);
|
||||
await sleep(500);
|
||||
expect(window.pageYOffset).toBe(0);
|
||||
scrollToSpy.mockRestore();
|
||||
});
|
||||
it('support onClick', async () => {
|
||||
const onClick = jest.fn();
|
||||
const wrapper = mount(BackTop, {
|
||||
propsData: {
|
||||
visibilityHeight: -1,
|
||||
},
|
||||
listeners: {
|
||||
click: onClick,
|
||||
},
|
||||
});
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x, y) => {
|
||||
window.scrollY = y;
|
||||
window.pageYOffset = y;
|
||||
});
|
||||
window.scrollTo(0, 400);
|
||||
// trigger scroll manually
|
||||
wrapper.vm.handleScroll();
|
||||
await sleep();
|
||||
wrapper.find('.ant-back-top').trigger('click');
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
scrollToSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
@ -1,20 +0,0 @@
|
||||
<cn>
|
||||
#### 基本
|
||||
最简单的用法。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### basic
|
||||
The most basic usage.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div>
|
||||
<a-back-top />
|
||||
Scroll down to see the bottom-right
|
||||
<strong style="color: rgba(64, 64, 64, 0.6)"> gray </strong>
|
||||
button.
|
||||
</div>
|
||||
</template>
|
||||
```
|
@ -1,37 +0,0 @@
|
||||
<cn>
|
||||
#### 自定义样式
|
||||
可以自定义回到顶部按钮的样式,限制宽高:`40px * 40px`。
|
||||
</cn>
|
||||
|
||||
<us>
|
||||
#### Custom style
|
||||
You can customize the style of the button, just note the size limit: no more than `40px * 40px`.
|
||||
</us>
|
||||
|
||||
```tpl
|
||||
<template>
|
||||
<div id="components-back-top-demo-custom">
|
||||
<a-back-top>
|
||||
<div class="ant-back-top-inner">UP</div>
|
||||
</a-back-top>
|
||||
Scroll down to see the bottom-right
|
||||
<strong style="color: #1088e9"> blue </strong>
|
||||
button.
|
||||
</div>
|
||||
</template>
|
||||
<style scoped>
|
||||
#components-back-top-demo-custom .ant-back-top {
|
||||
bottom: 100px;
|
||||
}
|
||||
#components-back-top-demo-custom .ant-back-top-inner {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
line-height: 40px;
|
||||
border-radius: 4px;
|
||||
background-color: #1088e9;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
</style>
|
||||
```
|
@ -1,41 +0,0 @@
|
||||
<script>
|
||||
import Basic from './basic';
|
||||
import Custom from './custom';
|
||||
import CN from '../index.zh-CN.md';
|
||||
import US from '../index.en-US.md';
|
||||
const md = {
|
||||
cn: `# BackTop 回到顶部
|
||||
返回页面顶部的操作按钮。
|
||||
## 何时使用
|
||||
- 当页面内容区域比较长时;
|
||||
- 当用户需要频繁返回顶部查看相关内容时。
|
||||
## 代码演示`,
|
||||
us: `# BackTop
|
||||
\`BackTop\` makes it easy to go back to the top of the page.
|
||||
## When To Use
|
||||
- When the page content is very long.
|
||||
- When you need to go back to the top very frequently in order to view the contents.
|
||||
## Examples
|
||||
`,
|
||||
};
|
||||
export default {
|
||||
category: 'Components',
|
||||
type: 'Other',
|
||||
zhType: '其他',
|
||||
subtitle: '回到顶部',
|
||||
title: 'BackTop',
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<md cn={md.cn} us={md.us} />
|
||||
<Basic />
|
||||
<Custom />
|
||||
<api>
|
||||
<CN slot="cn" />
|
||||
<US />
|
||||
</api>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
</script>
|
@ -1,16 +0,0 @@
|
||||
## API
|
||||
|
||||
> The distance to the bottom is set to `50px` by default, which is overridable.
|
||||
>
|
||||
> If you decide to use custom styles, please note the size limit: no more than `40px * 40px`.
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| target | specifies the scrollable area dom node | () => HTMLElement | () => window |
|
||||
| visibilityHeight | the `BackTop` button will not show until the scroll height reaches this value | number | 400 |
|
||||
|
||||
### events
|
||||
|
||||
| Events Name | Description | Arguments |
|
||||
| ----------- | -------------------------------------------------------------------- | --------- |
|
||||
| click | a callback function, which can be executed when you click the button | Function |
|
@ -1,22 +1,12 @@
|
||||
import raf from 'raf';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import addEventListener from '../_util/Dom/addEventListener';
|
||||
import addEventListener from '../vc-util/Dom/addEventListener';
|
||||
import getScroll from '../_util/getScroll';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import getTransitionProps from '../_util/getTransitionProps';
|
||||
import { ConfigConsumerProps } from '../config-provider';
|
||||
import Base from '../base';
|
||||
import { getListeners } from '../_util/props-util';
|
||||
|
||||
const easeInOutCubic = (t, b, c, d) => {
|
||||
const cc = c - b;
|
||||
t /= d / 2;
|
||||
if (t < 1) {
|
||||
return (cc / 2) * t * t * t + b;
|
||||
} else {
|
||||
return (cc / 2) * ((t -= 2) * t * t + 2) + b;
|
||||
}
|
||||
};
|
||||
import scrollTo from '../_util/scrollTo';
|
||||
|
||||
function getDefaultTarget() {
|
||||
return window;
|
||||
@ -70,33 +60,13 @@ const BackTop = {
|
||||
},
|
||||
|
||||
scrollToTop(e) {
|
||||
const scrollTop = this.getCurrentScrollTop();
|
||||
const startTime = Date.now();
|
||||
const frameFunc = () => {
|
||||
const timestamp = Date.now();
|
||||
const time = timestamp - startTime;
|
||||
this.setScrollTop(easeInOutCubic(time, scrollTop, 0, 450));
|
||||
if (time < 450) {
|
||||
raf(frameFunc);
|
||||
} else {
|
||||
this.setScrollTop(0);
|
||||
}
|
||||
};
|
||||
raf(frameFunc);
|
||||
const { target = getDefaultTarget } = this;
|
||||
scrollTo(0, {
|
||||
getContainer: target,
|
||||
});
|
||||
this.$emit('click', e);
|
||||
},
|
||||
|
||||
setScrollTop(value) {
|
||||
const getTarget = this.target || getDefaultTarget;
|
||||
const targetNode = getTarget();
|
||||
if (targetNode === window) {
|
||||
document.body.scrollTop = value;
|
||||
document.documentElement.scrollTop = value;
|
||||
} else {
|
||||
targetNode.scrollTop = value;
|
||||
}
|
||||
},
|
||||
|
||||
handleScroll() {
|
||||
const { visibilityHeight, target = getDefaultTarget } = this;
|
||||
const scrollTop = getScroll(target(), true);
|
||||
|
@ -1,16 +0,0 @@
|
||||
## API
|
||||
|
||||
> 有默认样式,距离底部 `50px`,可覆盖。
|
||||
>
|
||||
> 自定义样式宽高不大于 40px \* 40px。
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| target | 设置需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 | Function | () => window |
|
||||
| visibilityHeight | 滚动高度达到此参数值才出现 `BackTop` | number | 400 |
|
||||
|
||||
### 事件
|
||||
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
| -------- | ------------------ | -------- |
|
||||
| click | 点击按钮的回调函数 | Function |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user