From 9b57a027b7b3de19222b8be5e80789fef95e4243 Mon Sep 17 00:00:00 2001 From: myronliu347 Date: Mon, 26 Nov 2018 20:17:34 +0800 Subject: [PATCH 1/7] refactor(Cascader): add scroll to timeout --- src/cascader/menu.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cascader/menu.jsx b/src/cascader/menu.jsx index 00881a5f46..5ce7aebff3 100644 --- a/src/cascader/menu.jsx +++ b/src/cascader/menu.jsx @@ -19,11 +19,13 @@ export default class CascaderMenu extends Component { scrollToSelectedItem () { const { prefix, useVirtual, children } = this.props; if (useVirtual) { - if (!children || children.length === 0) return; + if (!children || children.length === 0) { + return; + } const selectedIndex = children.findIndex(item => !!item.props.selected); if (selectedIndex !== -1) { const instance = this.refs.virtual.getInstance(); - instance.scrollTo(selectedIndex); + setTimeout(() => instance.scrollTo(selectedIndex), 0); } } else { const itemSelector = `.${prefix}menu-item`; From ab109ebf20df22f883df2ac88411504c73a1b14c Mon Sep 17 00:00:00 2001 From: youluna Date: Tue, 27 Nov 2018 11:40:59 +0800 Subject: [PATCH 2/7] feat(VirtualList): consider as items of the same height by default --- docs/virtual-list/demo/basic.md | 2 - docs/virtual-list/demo/initial-index.md | 25 ++---- docs/virtual-list/demo/item-size-getter.md | 95 ++++++++++++++++++++++ docs/virtual-list/index.en-us.md | 2 +- docs/virtual-list/index.md | 2 +- src/virtual-list/virtual-list.jsx | 16 +++- 6 files changed, 115 insertions(+), 27 deletions(-) create mode 100644 docs/virtual-list/demo/item-size-getter.md diff --git a/docs/virtual-list/demo/basic.md b/docs/virtual-list/demo/basic.md index ffb156ee6a..90c5933004 100644 --- a/docs/virtual-list/demo/basic.md +++ b/docs/virtual-list/demo/basic.md @@ -32,8 +32,6 @@ const generateLi = (index = 'index') => { for (let i = 0; i < 1000; i++) { dataSource.push(generateLi(i)); } -console.log(dataSource) -const getHeight = index => index % 3 === 0 ? 20 : 30; const demo = (
diff --git a/docs/virtual-list/demo/initial-index.md b/docs/virtual-list/demo/initial-index.md index 01b52fb4df..1d70044b0a 100644 --- a/docs/virtual-list/demo/initial-index.md +++ b/docs/virtual-list/demo/initial-index.md @@ -1,13 +1,13 @@ # 设置初始位置 -- order: 0 +- order: 1 使用 jumpIndex 设置初始位置 :::lang=en-us # Basic -- order: 0 +- order: 1 Use jumpIndex to set first item. @@ -21,11 +21,7 @@ import { VirtualList } from '@alifd/next'; const dataSource = []; function generateLi(index) { - if (index % 3 === 0) { - return (
  • key-{index}
  • ); - } else { - return (
  • key-{index}
  • ); - } + return (
  • key-{index}
  • ); } function generateData(len) { for (let i = 0; i < len; i++) { @@ -35,20 +31,9 @@ function generateData(len) { class App extends React.Component { state = { - initial: 20, + initial: 50, dataSource: generateData(1000) } - componentDidMount() { - setTimeout(()=> { - const instance = this.refs.virtual.getInstance(); - instance.scrollTo(50); - }, 200); - - } - - getHeight(index) { - return index % 3 === 0 ? 30 : 20; - } onClick() { this.setState({ initial: this.state.initial + 20 @@ -61,7 +46,7 @@ class App extends React.Component {

    - + {dataSource}
    diff --git a/docs/virtual-list/demo/item-size-getter.md b/docs/virtual-list/demo/item-size-getter.md new file mode 100644 index 0000000000..70e6ec0dad --- /dev/null +++ b/docs/virtual-list/demo/item-size-getter.md @@ -0,0 +1,95 @@ +# 不等高的item + +- order: 2 + +使用 jumpIndex 设置初始位置, 并设置 itemSizeGetter + +:::lang=en-us +# Basic + +- order: 2 + +Use jumpIndex and itemSizeGetter to set first item in visual area. + +::: + +--- + +````jsx +import { VirtualList } from '@alifd/next'; + +const dataSource = []; + +function generateLi(index) { + if (index % 3 === 0) { + return (
  • key-{index}
  • ); + } else { + return (
  • key-{index}
  • ); + } +} +function generateData(len) { + for (let i = 0; i < len; i++) { + dataSource.push(generateLi(i)); + } +} + +class App extends React.Component { + state = { + initial: 20, + dataSource: generateData(1000) + } + + componentDidMount() { + setTimeout(()=> { + const instance = this.refs.virtual.getInstance(); + instance.scrollTo(50); + }, 200); + + } + + getHeight(index) { + return index % 3 === 0 ? 30 : 20; + } + + onClick() { + this.setState({ + initial: this.state.initial + 20 + }) + } + + render() { + return ( +
    + +
    +
    +
    + + {dataSource} + +
    +
    + ); + } +} + +ReactDOM.render(, mountNode); +```` + +````css +.virtual-box { + height: 200px; + width: 200px; + border: 1px solid #ddd; + overflow: auto; +} +.virtual-box ul { + padding: 0; + margin: 0; + list-style: none; +} +.virtual-box li { + padding-left: 10px; + border-bottom: 1px solid #333; +} +```` diff --git a/docs/virtual-list/index.en-us.md b/docs/virtual-list/index.en-us.md index 33bb6dfbb9..7e2ad9af53 100644 --- a/docs/virtual-list/index.en-us.md +++ b/docs/virtual-list/index.en-us.md @@ -18,4 +18,4 @@ | itemsRenderer | items parent dom,by default (items, ref) =>
      {items}


    **signature**:
    Function() => void | Function | (items, ref) => <ul ref={ref}>{items}</ul> | | threshold | height of threshold | Number | 100 | | itemSizeGetter | get item's height

    **signature**:
    Function() => void | Function | - | -| jumpIndex | set this to jump to the index you want, works only when you set itemSizeGetter | Number | 0 | +| jumpIndex | the index you want to jump to, set itemSizeGetter if the height of items vary | Number | 0 | diff --git a/docs/virtual-list/index.md b/docs/virtual-list/index.md index 36fa233474..954a80cddd 100644 --- a/docs/virtual-list/index.md +++ b/docs/virtual-list/index.md @@ -18,4 +18,4 @@ | itemsRenderer | 父渲染函数,默认为 (items, ref) =>
      {items}


    **签名**:
    Function() => void | Function | (items, ref) => <ul ref={ref}>{items}</ul> | | threshold | 缓冲区高度 | Number | 100 | | itemSizeGetter | 获取item高度的函数

    **签名**:
    Function() => void | Function | - | -| jumpIndex | 设置初始位置,需要设置 itemSizeGetter 才能生效 | Number | 0 | +| jumpIndex | 设置跳转位置,需要设置 itemSizeGetter 才能生效, 不设置认为元素等高并取第一个元素高度作为默认高 | Number | 0 | diff --git a/src/virtual-list/virtual-list.jsx b/src/virtual-list/virtual-list.jsx index c3a8799afc..074edfe03a 100644 --- a/src/virtual-list/virtual-list.jsx +++ b/src/virtual-list/virtual-list.jsx @@ -48,7 +48,7 @@ export default class VirtualList extends Component { */ itemSizeGetter: PropTypes.func, /** - * 设置跳转位置,需要设置 itemSizeGetter 才能生效 + * 设置跳转位置,需要设置 itemSizeGetter 才能生效, 不设置认为元素等高并取第一个元素高度作为默认高 */ jumpIndex: PropTypes.number, className: PropTypes.string @@ -153,7 +153,7 @@ export default class VirtualList extends Component { el = el.parentElement; switch (window.getComputedStyle(el).overflowY) { - case 'auto': case 'scroll': case 'overlay': return el; + case 'auto': case 'scroll': case 'overlay': case 'visible': return el; } return window; @@ -338,7 +338,8 @@ export default class VirtualList extends Component { getSizeOf(index) { const { cache } = this; - const { itemSizeGetter } = this.props; + const { itemSizeGetter, jumpIndex } = this.props; + // Try the cache. if (index in cache) { return cache[index]; @@ -346,6 +347,15 @@ export default class VirtualList extends Component { if (itemSizeGetter) { return itemSizeGetter(index); } + + const height = Object.values(this.cache).pop(); + if (!this.defaultItemHeight && jumpIndex > -1 && height) { + this.defaultItemHeight = height; + } + + if (this.defaultItemHeight) { + return this.defaultItemHeight; + } } constrain(from, size, {children, minSize}) { From bbba67919079a3336ed7ca34472e405741673121 Mon Sep 17 00:00:00 2001 From: youluna Date: Tue, 27 Nov 2018 11:43:47 +0800 Subject: [PATCH 3/7] feat(Select): support virtuallist in simple case --- docs/select/demo/virtual-select.md | 52 ++++++++++++++++++++++++++++++ src/select/base.jsx | 28 ++++++++++++---- src/select/main.scss | 12 +++++++ 3 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 docs/select/demo/virtual-select.md diff --git a/docs/select/demo/virtual-select.md b/docs/select/demo/virtual-select.md new file mode 100644 index 0000000000..9f4744b3c7 --- /dev/null +++ b/docs/select/demo/virtual-select.md @@ -0,0 +1,52 @@ +# 无限滚动 + +- order: 15 + +select 配合无限滚动 + +:::lang=en-us +# virtual-select + +- order: 15 + +select with virtual list +::: +--- + +````jsx +import { Select } from '@alifd/next'; + +const Option = Select.Option; + +const onChange = function (value) { + console.log(value); +}; + +function generateItem(index) { + return {label: `option${index}`, value: `option${index}`}; +} + +function generateOption(index) { + return ; +} + +function generateData(len, isOption) { + const data = []; + + for (let i = 0; i < len; i++) { + isOption ? data.push(generateOption(i)) : data.push(generateItem(i)); + } + + return data; +} + +ReactDOM.render( +
    + + {generateData(100, true)} + +
    + , mountNode); +```` diff --git a/src/select/base.jsx b/src/select/base.jsx index 66e52b88a0..5a422d97a0 100644 --- a/src/select/base.jsx +++ b/src/select/base.jsx @@ -7,6 +7,7 @@ import Menu from '../menu'; import Overlay from '../overlay'; import zhCN from '../locale/zh-cn'; import DataStore from './data-store'; +import VirtualList from '../virtual-list'; import { isSingle, filter, isNull, valueToSelectKey } from './util'; const { Popup } = Overlay; @@ -110,6 +111,10 @@ export default class Base extends React.Component { * 键盘上下键切换菜单高亮选项的回调 */ onToggleHighlightItem: PropTypes.func, + /** + * 是否开启虚拟滚动模式 + */ + useVirtual: PropTypes.bool, // 自定义类名 className: PropTypes.any, // children @@ -384,10 +389,6 @@ export default class Base extends React.Component { return null; } - saveMenuRef = (ref) => { - this.menuRef = ref; - }; - handleSelect() { } @@ -398,7 +399,7 @@ export default class Base extends React.Component { * @param {object} props */ renderMenu() { - const { prefix, mode, autoWidth, locale, notFoundContent } = this.props; + const { prefix, mode, autoWidth, locale, notFoundContent, useVirtual } = this.props; const { dataSource, highlightKey } = this.state; const value = this.state.value; let selectedKeys; @@ -425,7 +426,6 @@ export default class Base extends React.Component { } const menuProps = { - ref: this.saveMenuRef, children, style: autoWidth ? { width: this.width } : { minWidth: this.width }, selectedKeys, @@ -440,7 +440,21 @@ export default class Base extends React.Component { className: menuClassName }; - return ; + return ( + useVirtual ? +
    + { + return ( { + ref(c); + this.menuRef = c; + }} {...menuProps}>{items}); + }}> + {children} + +
    : + + ); } /** diff --git a/src/select/main.scss b/src/select/main.scss index b7fe4d2e1f..da61eed7f1 100644 --- a/src/select/main.scss +++ b/src/select/main.scss @@ -267,6 +267,18 @@ /* transform-origin: center 41.8%; // icon 图像中心 */ } + &-menu-wrapper { + max-height: 260px; + overflow: auto; + border: $popup-local-border-width $popup-local-border-style $popup-local-border-color; + border-radius: $popup-local-corner; + box-shadow: $popup-local-shadow; + #{$select-prefix}-menu { + max-height: none; + border: none; + } + } + /* 弹层 */ &-menu { max-height: 260px; From 9249d00eefa33b554be1be8ae95164e08ac27d17 Mon Sep 17 00:00:00 2001 From: youluna Date: Tue, 27 Nov 2018 11:58:30 +0800 Subject: [PATCH 4/7] temp(Cascader): use props.checked instead of props.selected --- src/cascader/menu.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cascader/menu.jsx b/src/cascader/menu.jsx index 5ce7aebff3..aca9b0cec0 100644 --- a/src/cascader/menu.jsx +++ b/src/cascader/menu.jsx @@ -22,7 +22,7 @@ export default class CascaderMenu extends Component { if (!children || children.length === 0) { return; } - const selectedIndex = children.findIndex(item => !!item.props.selected); + const selectedIndex = children.findIndex(item => !!item.props.checked); if (selectedIndex !== -1) { const instance = this.refs.virtual.getInstance(); setTimeout(() => instance.scrollTo(selectedIndex), 0); From 4181a78328aae48dc587df5e93d62846846af7ba Mon Sep 17 00:00:00 2001 From: myronliu347 Date: Tue, 27 Nov 2018 13:54:20 +0800 Subject: [PATCH 5/7] refactor(Cascader): change scrollToSelectedItem --- src/cascader/menu.jsx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/cascader/menu.jsx b/src/cascader/menu.jsx index aca9b0cec0..5d9625d312 100644 --- a/src/cascader/menu.jsx +++ b/src/cascader/menu.jsx @@ -18,19 +18,22 @@ export default class CascaderMenu extends Component { scrollToSelectedItem () { const { prefix, useVirtual, children } = this.props; + if (!children || children.length === 0) { + return; + } + const selectedIndex = children.findIndex(item => !!item.props.checked || !!item.props.selected || !!item.props.expanded); + + if (selectedIndex === -1) { + return; + } + if (useVirtual) { - if (!children || children.length === 0) { - return; - } - const selectedIndex = children.findIndex(item => !!item.props.checked); - if (selectedIndex !== -1) { - const instance = this.refs.virtual.getInstance(); - setTimeout(() => instance.scrollTo(selectedIndex), 0); - } + const instance = this.refs.virtual.getInstance(); + setTimeout(() => instance.scrollTo(selectedIndex), 0); } else { const itemSelector = `.${prefix}menu-item`; const menu = findDOMNode(this.refs.menu); - const targetItem = menu.querySelector(`${itemSelector}.${prefix}expanded, ${itemSelector}.${prefix}selected`); + const targetItem = menu.querySelectorAll(itemSelector)[selectedIndex]; if (targetItem) { menu.scrollTop = targetItem.offsetTop - Math.floor((menu.clientHeight / targetItem.clientHeight - 1) / 2) * targetItem.clientHeight; } From 340d72aac02cb438feaa87fe4c9498dcedb0d866 Mon Sep 17 00:00:00 2001 From: myronliu347 Date: Tue, 27 Nov 2018 14:09:44 +0800 Subject: [PATCH 6/7] test(Cascader): fix test unit --- test/cascader/index-spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cascader/index-spec.js b/test/cascader/index-spec.js index 06055048f5..d84ecb1280 100644 --- a/test/cascader/index-spec.js +++ b/test/cascader/index-spec.js @@ -381,7 +381,7 @@ describe('Cascader', () => { it('should support listClassName and listStyle', () => { wrapper = mount(); - const list = wrapper.find('ul.next-cascader-menu'); + const list = wrapper.find('div.next-cascader-menu-wrapper'); assert(list.prop('style').width === '400px'); assert(list.prop('style').height === '400px'); assert(list.hasClass('custom')); @@ -415,7 +415,7 @@ describe('Cascader', () => { const inner = document.querySelector('#cascader-style .next-cascader-inner'); assert(inner.style.width === '600px'); - const lists = document.querySelectorAll('.next-cascader-menu'); + const lists = document.querySelectorAll('.next-cascader-menu-wrapper'); assert(lists[lists.length - 1].className.indexOf('next-has-right-border') > -1); ReactDOM.unmountComponentAtNode(div); From 8d860e8726e502871af566e1e6835d6590f441e1 Mon Sep 17 00:00:00 2001 From: youluna Date: Tue, 27 Nov 2018 14:27:22 +0800 Subject: [PATCH 7/7] test(VirtualList): add test cases --- test/virtual-list/index-spec.js | 76 +++++++++++++++++++++------------ 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/test/virtual-list/index-spec.js b/test/virtual-list/index-spec.js index 99c30ab4db..8d00f40e98 100644 --- a/test/virtual-list/index-spec.js +++ b/test/virtual-list/index-spec.js @@ -3,7 +3,8 @@ import ReactDOM from 'react-dom'; import Enzyme from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; import assert from 'power-assert'; -import VirtualList from '../src/index'; +import VirtualList from '../../src/virtual-list/index'; +import '../../src/virtual-list/style.js'; Enzyme.configure({ adapter: new Adapter() }); @@ -36,7 +37,7 @@ const generateData = (len) => { const dataSource = []; for (let i = 0; i < len; i++) { - dataSource.push(
  • {i}-test
  • ); + dataSource.push(
  • {i}
  • ); } return dataSource; @@ -55,19 +56,17 @@ describe('VirtualList', () => { it('should render', () => { - class App extends React.Component { - render() { - return ( -
    - - {generateData(10)} - -
    - ); - } + function App() { + return ( +
    + + {generateData(10)} + +
    + ); } wrapper = render(); @@ -75,23 +74,44 @@ describe('VirtualList', () => { }); it('should render much more', () => { - class App extends React.Component { - render() { - return ( -
    - - {generateData(100)} - -
    - ); - } + function App() { + return ( +
    + + {generateData(100)} + +
    + ); } wrapper = render(); assert(wrapper.find('li').length < 20); }); + it('should support jumpIndex', (done) => { + function App() { + return ( +
    + { + return 20; + }}> + {generateData(100)} + +
    + ); + } + + wrapper = render(); + setTimeout(() => { + assert(wrapper.find('li')[0].innerText > 40); + done(); + }, 100); + }); + });