add affix demo

pull/9/head
tangjinzhou 2018-03-02 16:54:22 +08:00
parent 08f9f6afbb
commit dd1266ee61
20 changed files with 829 additions and 43 deletions

View File

@ -0,0 +1,23 @@
<cn>
#### 基本
最简单的用法。
</cn>
<us>
#### basic
The simplest usage.
</us>
```html
<template>
<div>
<a-affix>
<a-button type="primary">Affix top</a-button>
</a-affix>
<br />
<a-affix :offsetBottom="0">
<a-button type="primary">Affix bottom</a-button>
</a-affix>
</div>
</template>
```

View File

@ -0,0 +1,37 @@
<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.
`,
}
export default {
render () {
return (
<div>
<md cn={md.cn} us={md.us}/>
<Basic />
<Onchange />
<Target />
<api>
<CN slot='cn' />
<US/>
</api>
</div>
)
},
}
</script>

View File

@ -0,0 +1,26 @@
<cn>
#### 固定状态改变的回调
可以获得是否固定的状态。
</cn>
<us>
#### Callback
Callback with affixed state.
</us>
```html
<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>
```

View File

@ -0,0 +1,34 @@
<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>
```html
<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>
```

View File

@ -0,0 +1,21 @@
## 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>
````

View File

@ -5,7 +5,8 @@ import classNames from 'classnames'
import shallowequal from 'shallowequal'
import omit from 'omit.js'
import getScroll from '../_util/getScroll'
import { throttleByAnimationFrameDecorator } from '../_util/throttleByAnimationFrame'
import BaseMixin from '../_util/BaseMixin'
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame'
function getTargetRect (target) {
return target !== window
@ -55,14 +56,10 @@ const AffixProps = {
prefixCls: PropTypes.string,
}
// export interface AffixState {
// affixStyle: React.CSSProperties | undefined;
// placeholderStyle: React.CSSProperties | undefined;
// }
export default {
name: 'Affix',
props: AffixProps,
mixins: [BaseMixin],
data () {
this.events = [
'resize',
@ -79,6 +76,9 @@ export default {
placeholderStyle: undefined,
}
},
beforeMount () {
this.updatePosition = throttleByAnimationFrame(this.updatePosition)
},
mounted () {
const target = this.target || getDefaultTarget
// Wait for parent component ref has its value
@ -96,7 +96,6 @@ export default {
})
},
},
beforeDestroy () {
this.clearEventListeners()
clearTimeout(this.timeout)
@ -130,7 +129,6 @@ export default {
this.setState({ placeholderStyle: placeholderStyle })
},
// @throttleByAnimationFrameDecorator()
updatePosition (e) {
let { offsetTop } = this
const { offsetBottom, offset, target = getDefaultTarget } = this

View File

@ -0,0 +1,23 @@
## 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>
````

View File

@ -11,7 +11,7 @@ There are `primary` button, `default` button, `dashed` button and `danger` butto
```html
<template>
<div class='test'>
<div>
<a-button type="primary">Primary</a-button>
<a-button>Default</a-button>
<a-button type="dashed">Dashed</a-button>

View File

@ -1,9 +1,8 @@
<script>
import { Tabs } from 'antd'
import omit from 'omit.js'
import PropTypes from '../_util/vue-types'
import addEventListener from '../_util/Dom/addEventListener'
import { hasProp, getComponentFromProp, getComponentName } from '../_util/props-util'
import { getComponentFromProp, getComponentName } from '../_util/props-util'
import throttleByAnimationFrame from '../_util/throttleByAnimationFrame'
import BaseMixin from '../_util/BaseMixin'
@ -18,32 +17,25 @@
bordered: PropTypes.bool.def(true),
bodyStyle: PropTypes.object,
loading: PropTypes.bool.def(false),
noHovering: PropTypes.bool.def(false),
hoverable: PropTypes.bool.def(false),
type: PropTypes.string,
actions: PropTypes.any,
tabList: PropTypes.array,
},
data () {
this.updateWiderPaddingCalled = false
return {
widerPadding: false,
updateWiderPaddingCalled: false,
}
},
mounted () {
beforeMount () {
this.updateWiderPadding = throttleByAnimationFrame(this.updateWiderPadding)
},
mounted () {
this.updateWiderPadding()
this.resizeEvent = addEventListener(window, 'resize', this.updateWiderPadding)
// if (hasProp(this, 'noHovering')) {
// warning(
// !this.noHovering,
// '`noHovering` of Card is deperated, you can remove it safely or use `hoverable` instead.',
// )
// warning(!!this.noHovering, '`noHovering={false}` of Card is deperated, use `hoverable` instead.')
// }
},
beforeMount () {
beforeDestroy () {
if (this.resizeEvent) {
this.resizeEvent.remove()
}
@ -84,19 +76,11 @@
})
return containGrid
},
// For 2.x compatible
getCompatibleHoverable () {
const { noHovering, hoverable } = this.$props
if (hasProp(this, 'noHovering')) {
return !noHovering || hoverable
}
return !!hoverable
},
},
render () {
const {
prefixCls = 'ant-card', extra, bodyStyle, title, loading,
bordered = true, type, tabList, ...others
bordered = true, type, tabList, hoverable,
} = this.$props
const { $slots } = this
@ -105,7 +89,7 @@
[`${prefixCls}`]: true,
[`${prefixCls}-loading`]: loading,
[`${prefixCls}-bordered`]: bordered,
[`${prefixCls}-hoverable`]: this.getCompatibleHoverable(),
[`${prefixCls}-hoverable`]: !!hoverable,
[`${prefixCls}-wider-padding`]: this.widerPadding,
[`${prefixCls}-padding-transition`]: this.updateWiderPaddingCalled,
[`${prefixCls}-contain-grid`]: this.isContainGrid($slots.default),
@ -166,11 +150,9 @@
)
const actions = getComponentFromProp(this, 'actions')
const actionDom = actions || null
const divProps = omit(others, [
'tabChange',
])
return (
<div {...divProps} class={classString} ref='cardContainerRef'>
<div class={classString} ref='cardContainerRef'>
{head}
{coverDom}
{children ? body : null}

View File

@ -89,3 +89,5 @@ export { default as LocaleProvider } from './locale-provider'
export { default as AutoComplete } from './auto-complete'
export { default as Affix } from './affix'

View File

@ -24,3 +24,4 @@ import './spin/style'
import './select/style'
import './switch/style'
import './auto-complete/style'
import './affix/style'

View File

@ -0,0 +1,313 @@
<script>
import React, { Component, cloneElement } from 'react';
import PropTypes from 'prop-types';
import Trigger from 'rc-trigger';
import Menus from './Menus';
import KeyCode from 'rc-util/lib/KeyCode';
import arrayTreeFilter from 'array-tree-filter';
import shallowEqualArrays from 'shallow-equal/arrays';
const BUILT_IN_PLACEMENTS = {
bottomLeft: {
points: ['tl', 'bl'],
offset: [0, 4],
overflow: {
adjustX: 1,
adjustY: 1,
},
},
topLeft: {
points: ['bl', 'tl'],
offset: [0, -4],
overflow: {
adjustX: 1,
adjustY: 1,
},
},
bottomRight: {
points: ['tr', 'br'],
offset: [0, 4],
overflow: {
adjustX: 1,
adjustY: 1,
},
},
topRight: {
points: ['br', 'tr'],
offset: [0, -4],
overflow: {
adjustX: 1,
adjustY: 1,
},
},
};
class Cascader extends Component {
constructor(props) {
super(props);
let initialValue = [];
if ('value' in props) {
initialValue = props.value || [];
} else if ('defaultValue' in props) {
initialValue = props.defaultValue || [];
}
this.state = {
popupVisible: props.popupVisible,
activeValue: initialValue,
value: initialValue,
};
}
componentWillReceiveProps(nextProps) {
if ('value' in nextProps && !shallowEqualArrays(this.props.value, nextProps.value)) {
const newValues = {
value: nextProps.value || [],
activeValue: nextProps.value || [],
};
// allow activeValue diff from value
// https://github.com/ant-design/ant-design/issues/2767
if ('loadData' in nextProps) {
delete newValues.activeValue;
}
this.setState(newValues);
}
if ('popupVisible' in nextProps) {
this.setState({
popupVisible: nextProps.popupVisible,
});
}
}
getPopupDOMNode() {
return this.trigger.getPopupDomNode();
}
getCurrentLevelOptions() {
const { options } = this.props;
const { activeValue = [] } = this.state;
const result = arrayTreeFilter(options, (o, level) => o.value === activeValue[level]);
if (result[result.length - 2]) {
return result[result.length - 2].children;
}
return [...options].filter(o => !o.disabled);
}
getActiveOptions(activeValue) {
return arrayTreeFilter(this.props.options, (o, level) => o.value === activeValue[level]);
}
setPopupVisible = (popupVisible) => {
if (!('popupVisible' in this.props)) {
this.setState({ popupVisible });
}
// sync activeValue with value when panel open
if (popupVisible && !this.state.visible) {
this.setState({
activeValue: this.state.value,
});
}
this.props.onPopupVisibleChange(popupVisible);
}
handleChange = (options, setProps, e) => {
if (e.type !== 'keydown' || e.keyCode === KeyCode.ENTER) {
this.props.onChange(options.map(o => o.value), options);
this.setPopupVisible(setProps.visible);
}
}
handlePopupVisibleChange = (popupVisible) => {
this.setPopupVisible(popupVisible);
}
handleMenuSelect = (targetOption, menuIndex, e) => {
// Keep focused state for keyboard support
const triggerNode = this.trigger.getRootDomNode();
if (triggerNode && triggerNode.focus) {
triggerNode.focus();
}
const { changeOnSelect, loadData, expandTrigger } = this.props;
if (!targetOption || targetOption.disabled) {
return;
}
let { activeValue } = this.state;
activeValue = activeValue.slice(0, menuIndex + 1);
activeValue[menuIndex] = targetOption.value;
const activeOptions = this.getActiveOptions(activeValue);
if (targetOption.isLeaf === false && !targetOption.children && loadData) {
if (changeOnSelect) {
this.handleChange(activeOptions, { visible: true }, e);
}
this.setState({ activeValue });
loadData(activeOptions);
return;
}
const newState = {};
if (!targetOption.children || !targetOption.children.length) {
this.handleChange(activeOptions, { visible: false }, e);
// set value to activeValue when select leaf option
newState.value = activeValue;
// add e.type judgement to prevent `onChange` being triggered by mouseEnter
} else if (changeOnSelect && (e.type === 'click' || e.type === 'keydown')) {
if (expandTrigger === 'hover') {
this.handleChange(activeOptions, { visible: false }, e);
} else {
this.handleChange(activeOptions, { visible: true }, e);
}
// set value to activeValue on every select
newState.value = activeValue;
}
newState.activeValue = activeValue;
// not change the value by keyboard
if ('value' in this.props ||
(e.type === 'keydown' && e.keyCode !== KeyCode.ENTER)) {
delete newState.value;
}
this.setState(newState);
}
handleKeyDown = (e) => {
const { children } = this.props;
// https://github.com/ant-design/ant-design/issues/6717
// Don't bind keyboard support when children specify the onKeyDown
if (children && children.props.onKeyDown) {
children.props.onKeyDown(e);
return;
}
const activeValue = [...this.state.activeValue];
const currentLevel = activeValue.length - 1 < 0 ? 0 : activeValue.length - 1;
const currentOptions = this.getCurrentLevelOptions();
const currentIndex = currentOptions.map(o => o.value).indexOf(activeValue[currentLevel]);
if (e.keyCode !== KeyCode.DOWN &&
e.keyCode !== KeyCode.UP &&
e.keyCode !== KeyCode.LEFT &&
e.keyCode !== KeyCode.RIGHT &&
e.keyCode !== KeyCode.ENTER &&
e.keyCode !== KeyCode.BACKSPACE &&
e.keyCode !== KeyCode.ESC) {
return;
}
// Press any keys above to reopen menu
if (!this.state.popupVisible &&
e.keyCode !== KeyCode.BACKSPACE &&
e.keyCode !== KeyCode.LEFT &&
e.keyCode !== KeyCode.RIGHT &&
e.keyCode !== KeyCode.ESC) {
this.setPopupVisible(true);
return;
}
if (e.keyCode === KeyCode.DOWN || e.keyCode === KeyCode.UP) {
let nextIndex = currentIndex;
if (nextIndex !== -1) {
if (e.keyCode === KeyCode.DOWN) {
nextIndex += 1;
nextIndex = nextIndex >= currentOptions.length ? 0 : nextIndex;
} else {
nextIndex -= 1;
nextIndex = nextIndex < 0 ? currentOptions.length - 1 : nextIndex;
}
} else {
nextIndex = 0;
}
activeValue[currentLevel] = currentOptions[nextIndex].value;
} else if (e.keyCode === KeyCode.LEFT || e.keyCode === KeyCode.BACKSPACE) {
activeValue.splice(activeValue.length - 1, 1);
} else if (e.keyCode === KeyCode.RIGHT) {
if (currentOptions[currentIndex] && currentOptions[currentIndex].children) {
activeValue.push(currentOptions[currentIndex].children[0].value);
}
} else if (e.keyCode === KeyCode.ESC) {
this.setPopupVisible(false);
return;
}
if (!activeValue || activeValue.length === 0) {
this.setPopupVisible(false);
}
const activeOptions = this.getActiveOptions(activeValue);
const targetOption = activeOptions[activeOptions.length - 1];
this.handleMenuSelect(targetOption, activeOptions.length - 1, e);
if (this.props.onKeyDown) {
this.props.onKeyDown(e);
}
}
saveTrigger = (node) => {
this.trigger = node;
}
render() {
const {
prefixCls, transitionName, popupClassName, options, disabled,
builtinPlacements, popupPlacement, children, ...restProps,
} = this.props;
// Did not show popup when there is no options
let menus = <div />;
let emptyMenuClassName = '';
if (options && options.length > 0) {
menus = (
<Menus
{...this.props}
value={this.state.value}
activeValue={this.state.activeValue}
onSelect={this.handleMenuSelect}
visible={this.state.popupVisible}
/>
);
} else {
emptyMenuClassName = ` ${prefixCls}-menus-empty`;
}
return (
<Trigger
ref={this.saveTrigger}
{...restProps}
options={options}
disabled={disabled}
popupPlacement={popupPlacement}
builtinPlacements={builtinPlacements}
popupTransitionName={transitionName}
action={disabled ? [] : ['click']}
popupVisible={disabled ? false : this.state.popupVisible}
onPopupVisibleChange={this.handlePopupVisibleChange}
prefixCls={`${prefixCls}-menus`}
popupClassName={popupClassName + emptyMenuClassName}
popup={menus}
>
{cloneElement(children, {
onKeyDown: this.handleKeyDown,
tabIndex: disabled ? undefined : 0,
})}
</Trigger>
);
}
}
Cascader.defaultProps = {
options: [],
onChange() {},
onPopupVisibleChange() {},
disabled: false,
transitionName: '',
prefixCls: 'rc-cascader',
popupClassName: '',
popupPlacement: 'bottomLeft',
builtinPlacements: BUILT_IN_PLACEMENTS,
expandTrigger: 'click',
};
Cascader.propTypes = {
value: PropTypes.array,
defaultValue: PropTypes.array,
options: PropTypes.array.isRequired,
onChange: PropTypes.func,
onPopupVisibleChange: PropTypes.func,
popupVisible: PropTypes.bool,
disabled: PropTypes.bool,
transitionName: PropTypes.string,
popupClassName: PropTypes.string,
popupPlacement: PropTypes.string,
prefixCls: PropTypes.string,
dropdownMenuColumnStyle: PropTypes.object,
builtinPlacements: PropTypes.object,
loadData: PropTypes.func,
changeOnSelect: PropTypes.bool,
children: PropTypes.node,
onKeyDown: PropTypes.func,
expandTrigger: PropTypes.string,
};
export default Cascader;
</script>

View File

@ -0,0 +1,156 @@
<script>
import React from 'react'
import PropTypes from 'prop-types'
import arrayTreeFilter from 'array-tree-filter'
import { findDOMNode } from 'react-dom'
class Menus extends React.Component {
constructor (props) {
super(props)
this.menuItems = {}
}
componentDidMount () {
this.scrollActiveItemToView()
}
componentDidUpdate (prevProps) {
if (!prevProps.visible && this.props.visible) {
this.scrollActiveItemToView()
}
}
getOption (option, menuIndex) {
const { prefixCls, expandTrigger } = this.props
const onSelect = this.props.onSelect.bind(this, option, menuIndex)
let expandProps = {
onClick: onSelect,
}
let menuItemCls = `${prefixCls}-menu-item`
const hasChildren = option.children && option.children.length > 0
if (hasChildren || option.isLeaf === false) {
menuItemCls += ` ${prefixCls}-menu-item-expand`
}
if (expandTrigger === 'hover' && hasChildren) {
expandProps = {
onMouseEnter: this.delayOnSelect.bind(this, onSelect),
onMouseLeave: this.delayOnSelect.bind(this),
onClick: onSelect,
}
}
if (this.isActiveOption(option, menuIndex)) {
menuItemCls += ` ${prefixCls}-menu-item-active`
expandProps.ref = this.saveMenuItem(menuIndex)
}
if (option.disabled) {
menuItemCls += ` ${prefixCls}-menu-item-disabled`
}
if (option.loading) {
menuItemCls += ` ${prefixCls}-menu-item-loading`
}
let title = ''
if (option.title) {
title = option.title
} else if (typeof option.label === 'string') {
title = option.label
}
return (
<li
key={option.value}
className={menuItemCls}
title={title}
{...expandProps}
>
{option.label}
</li>
)
}
getActiveOptions (values) {
const activeValue = values || this.props.activeValue
const options = this.props.options
return arrayTreeFilter(options, (o, level) => o.value === activeValue[level])
}
getShowOptions () {
const { options } = this.props
const result = this.getActiveOptions()
.map(activeOption => activeOption.children)
.filter(activeOption => !!activeOption)
result.unshift(options)
return result
}
delayOnSelect (onSelect, ...args) {
if (this.delayTimer) {
clearTimeout(this.delayTimer)
this.delayTimer = null
}
if (typeof onSelect === 'function') {
this.delayTimer = setTimeout(() => {
onSelect(args)
this.delayTimer = null
}, 150)
}
}
scrollActiveItemToView () {
// scroll into view
const optionsLength = this.getShowOptions().length
for (let i = 0; i < optionsLength; i++) {
const itemComponent = this.menuItems[i]
if (itemComponent) {
const target = findDOMNode(itemComponent)
target.parentNode.scrollTop = target.offsetTop
}
}
}
isActiveOption (option, menuIndex) {
const { activeValue = [] } = this.props
return activeValue[menuIndex] === option.value
}
saveMenuItem = (index) => (node) => {
this.menuItems[index] = node
}
render () {
const { prefixCls, dropdownMenuColumnStyle } = this.props
return (
<div>
{this.getShowOptions().map((options, menuIndex) =>
<ul className={`${prefixCls}-menu`} key={menuIndex} style={dropdownMenuColumnStyle}>
{options.map(option => this.getOption(option, menuIndex))}
</ul>
)}
</div>
)
}
}
Menus.defaultProps = {
options: [],
value: [],
activeValue: [],
onSelect () {},
prefixCls: 'rc-cascader-menus',
visible: false,
expandTrigger: 'click',
}
Menus.propTypes = {
value: PropTypes.array,
activeValue: PropTypes.array,
options: PropTypes.array.isRequired,
prefixCls: PropTypes.string,
expandTrigger: PropTypes.string,
onSelect: PropTypes.func,
visible: PropTypes.bool,
dropdownMenuColumnStyle: PropTypes.object,
}
export default Menus
</script>

View File

@ -0,0 +1,167 @@
.effect() {
animation-duration: .3s;
animation-fill-mode: both;
transform-origin: 0 0;
}
.rc-cascader {
font-size: 12px;
&-menus {
font-size: 12px;
overflow: hidden;
background: #fff;
position: absolute;
border: 1px solid #d9d9d9;
border-radius: 6px;
box-shadow: 0 0 4px rgba(0,0,0,0.17);
white-space: nowrap;
&-hidden {
display: none;
}
&.slide-up-enter, &.slide-up-appear {
.effect();
opacity: 0;
animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
animation-play-state: paused;
}
&.slide-up-leave {
.effect();
opacity: 1;
animation-timing-function: cubic-bezier(0.6, 0.04, 0.98, 0.34);
animation-play-state: paused;
}
&.slide-up-enter.slide-up-enter-active&-placement-bottomLeft,
&.slide-up-appear.slide-up-appear-active&-placement-bottomLeft {
animation-name: SlideUpIn;
animation-play-state: running;
}
&.slide-up-enter.slide-up-enter-active&-placement-topLeft,
&.slide-up-appear.slide-up-appear-active&-placement-topLeft {
animation-name: SlideDownIn;
animation-play-state: running;
}
&.slide-up-leave.slide-up-leave-active&-placement-bottomLeft {
animation-name: SlideUpOut;
animation-play-state: running;
}
&.slide-up-leave.slide-up-leave-active&-placement-topLeft {
animation-name: SlideDownOut;
animation-play-state: running;
}
}
&-menu {
display: inline-block;
width: 100px;
height: 192px;
list-style: none;
margin: 0;
padding: 0;
border-right: 1px solid #e9e9e9;
overflow: auto;
&:last-child {
border-right: 0;
}
}
&-menu-item {
height: 32px;
line-height: 32px;
padding: 0 16px;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
transition: all 0.3s ease;
position: relative;
&:hover {
background: tint(#2db7f5, 90%);
}
&-disabled {
cursor: not-allowed;
color: #ccc;
&:hover {
background: transparent;
}
}
&-loading:after {
position: absolute;
right: 12px;
content: 'loading';
color: #aaa;
font-style: italic;
}
&-active {
background: tint(#2db7f5, 80%);
&:hover {
background: tint(#2db7f5, 80%);
}
}
&-expand {
position: relative;
&:after {
content: '>';
font-size: 12px;
color: #999;
position: absolute;
right: 16px;
line-height: 32px;
}
}
}
}
@keyframes SlideUpIn {
0% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(.8);
}
100% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
}
@keyframes SlideUpOut {
0% {
opacity: 1;
transform-origin: 0% 0%;
transform: scaleY(1);
}
100% {
opacity: 0;
transform-origin: 0% 0%;
transform: scaleY(0.8);
}
}
@keyframes SlideDownIn {
0% {
opacity: 0;
transform-origin: 0% 100%;
transform: scaleY(0.8);
}
100% {
opacity: 1;
transform-origin: 0% 100%;
transform: scaleY(1);
}
}
@keyframes SlideDownOut {
0% {
opacity: 1;
transform-origin: 0% 100%;
transform: scaleY(1);
}
100% {
opacity: 0;
transform-origin: 0% 100%;
transform: scaleY(0.8);
}
}

View File

@ -0,0 +1,3 @@
// export this package's api
import Cascader from './Cascader'
export default Cascader

View File

@ -15,9 +15,6 @@ export default {
props: {
...switchPropTypes,
prefixCls: switchPropTypes.prefixCls.def('rc-switch'),
checkedChildren: switchPropTypes.checkedChildren,
unCheckedChildren: switchPropTypes.unCheckedChildren,
defaultChecked: switchPropTypes.defaultChecked.def(''),
// onChange: switchPropTypes.onChange.def(noop),
// onClick: switchPropTypes.onClick.def(noop),
},

View File

@ -19,6 +19,8 @@ Select | done
Input | done
InputNumber
AutoComplete | done
Affix | done
Cascader
Modal
Alert
Calendar
@ -32,7 +34,6 @@ Mention
##万
Grid
Col
Affix
BackTop
Layout
Anchor

View File

@ -25,4 +25,5 @@ export { default as message } from 'antd/message/demo/index.vue'
export { default as spin } from 'antd/spin/demo/index.vue'
export { default as switch } from 'antd/switch/demo/index.vue'
export { default as autoComplete } from 'antd/auto-complete/demo/index.vue'
export { default as affix } from 'antd/Affix/demo/index.vue'

View File

@ -3,7 +3,7 @@ const AsyncComp = () => {
const hashs = window.location.hash.split('/')
const d = hashs[hashs.length - 1]
return {
component: import(`../components/auto-complete/demo/${d}`),
component: import(`../components/affix/demo/${d}`),
}
}
export default [

View File

@ -100,6 +100,7 @@
"lodash.isplainobject": "^4.0.6",
"moment": "^2.20.1",
"omit.js": "^1.0.0",
"shallowequal": "^1.0.2",
"vue": "^2.5.13",
"vue-clipboard2": "0.0.8",
"vue-types": "^1.0.2",