Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions examples/animate.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.motion {
transition: all .3s;
}

.item {
display: inline-block;
box-sizing: border-box;
margin: 0;
padding: 0 16px;
overflow: hidden;
line-height: 31px;
position: relative;

&::after {
content: '';
border-bottom: 1px solid gray;
position: absolute;
bottom: 0;
left: 0;
right: 0;
}

button {
vertical-align: text-top;
margin-right: 8px;
}
}
182 changes: 182 additions & 0 deletions examples/animate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
import * as React from 'react';
// @ts-ignore
import CSSMotion from 'rc-animate/lib/CSSMotion';
import classNames from 'classnames';
import List, { ScrollInfo } from '../src/List';
import './animate.less';

let uuid = 0;
function genItem() {
const item = {
id: `key_${uuid}`,
uuid,
};
uuid += 1;
return item;
}

const originDataSource: Item[] = [];
for (let i = 0; i < 100000; i += 1) {
originDataSource.push(genItem());
}

interface Item {
id: string;
uuid: number;
}

interface MyItemProps extends Item {
visible: boolean;
motionAppear: boolean;
onClose: (id: string) => void;
onLeave: (id: string) => void;
onAppear: (...args: any[]) => void;
onInsertBefore: (id: string) => void;
onInsertAfter: (id: string) => void;
}

const getCurrentHeight = (node: HTMLElement) => ({ height: node.offsetHeight });
const getMaxHeight = (node: HTMLElement) => {
return { height: node.scrollHeight };
};
const getCollapsedHeight = () => ({ height: 0, opacity: 0 });

const MyItem: React.FC<MyItemProps> = (
{ id, uuid, visible, onClose, onLeave, onAppear, onInsertBefore, onInsertAfter, motionAppear },
ref,
) => {
return (
<CSSMotion
visible={visible}
ref={ref}
motionName="motion"
motionAppear={motionAppear}
onAppearStart={getCollapsedHeight}
onAppearActive={getMaxHeight}
onAppearEnd={onAppear}
onLeaveStart={getCurrentHeight}
onLeaveActive={getCollapsedHeight}
onLeaveEnd={() => {
onLeave(id);
}}
>
{({ className, style }, motionRef) => {
// if (uuid >= 100) {
// console.log('=>', id, className, style);
// }
return (
<div ref={motionRef} className={classNames('item', className)} style={style} data-id={id}>
<div style={{ height: uuid % 2 ? 100 : undefined }}>
<button
onClick={() => {
onClose(id);
}}
>
Close
</button>
<button
onClick={() => {
onInsertBefore(id);
}}
>
Insert Before
</button>
<button
onClick={() => {
onInsertAfter(id);
}}
>
Insert After
</button>
{id}
</div>
</div>
);
}}
</CSSMotion>
);
};

const ForwardMyItem = React.forwardRef(MyItem);

const Demo = () => {
const [dataSource, setDataSource] = React.useState(originDataSource);
const [closeMap, setCloseMap] = React.useState<{ [id: number]: boolean }>({});
const [animating, setAnimating] = React.useState(false);
const [insertIndex, setInsertIndex] = React.useState<number>();

const listRef = React.useRef<List<Item>>();

const onClose = (id: string) => {
setCloseMap({
...closeMap,
[id]: true,
});
};

const onLeave = (id: string) => {
const newDataSource = dataSource.filter(item => item.id !== id);
setDataSource(newDataSource);
};

const onAppear = (...args: any[]) => {
setAnimating(false);
};

function lockForAnimation() {
setAnimating(true);
}

const onInsertBefore = (id: string) => {
const index = dataSource.findIndex(item => item.id === id);
const newDataSource = [...dataSource.slice(0, index), genItem(), ...dataSource.slice(index)];
setInsertIndex(index);
setDataSource(newDataSource);
lockForAnimation();
};
const onInsertAfter = (id: string) => {
const index = dataSource.findIndex(item => item.id === id) + 1;
const newDataSource = [...dataSource.slice(0, index), genItem(), ...dataSource.slice(index)];
setInsertIndex(index);
setDataSource(newDataSource);
lockForAnimation();
};

return (
<React.StrictMode>
<div>
<h2>Animate</h2>
<p>Current: {dataSource.length} records</p>

<List<Item>
dataSource={dataSource}
data-id="list"
height={200}
itemHeight={30}
itemKey="id"
disabled={animating}
ref={listRef}
style={{
border: '1px solid red',
boxSizing: 'border-box',
}}
>
{(item, index) => (
<ForwardMyItem
{...item}
motionAppear={animating && insertIndex === index}
visible={!closeMap[item.id]}
onClose={onClose}
onLeave={onLeave}
onAppear={onAppear}
onInsertBefore={onInsertBefore}
onInsertAfter={onInsertAfter}
/>
)}
</List>
</div>
</React.StrictMode>
);
};

export default Demo;
3 changes: 2 additions & 1 deletion examples/basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const MyItem: React.FC<Item> = ({ id }, ref) => {

const ForwardMyItem = React.forwardRef(MyItem);

class TestItem extends React.Component {
class TestItem extends React.Component<{ id: number }> {
render() {
return <div style={{ lineHeight: '30px' }}>{this.props.id}</div>;
}
Expand Down Expand Up @@ -68,6 +68,7 @@ const Demo = () => {
dataSource={dataSource}
height={200}
itemHeight={30}
itemKey="id"
style={{
border: '1px solid red',
boxSizing: 'border-box',
Expand Down
8 changes: 3 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,22 @@
"react-dom": "*"
},
"devDependencies": {
"@types/lodash": "^4.14.135",
"@types/react": "^16.8.19",
"@types/react-dom": "^16.8.4",
"@types/warning": "^3.0.0",
"classnames": "^2.2.6",
"cross-env": "^5.2.0",
"enzyme": "^3.1.0",
"enzyme-adapter-react-16": "^1.0.2",
"enzyme-to-json": "^3.1.4",
"father": "^2.13.2",
"np": "^5.0.3",
"rc-animate": "^2.9.1",
"react": "^v16.9.0-alpha.0",
"react-dom": "^v16.9.0-alpha.0",
"typescript": "^3.5.2"
},
"dependencies": {
"async-validator": "^1.11.2",
"lodash": "^4.17.4",
"rc-util": "^4.6.0",
"warning": "^4.0.3"
"rc-util": "^4.8.0"
}
}
44 changes: 27 additions & 17 deletions src/Filler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,40 @@ interface FillerProps {
/** Virtual filler height. Should be `count * itemMinHeight` */
height: number;
/** Set offset of visible items. Should be the top of start item position */
offset: number;
offset?: number;

children: React.ReactNode;
}

/**
* Fill component to provided the scroll content real height.
*/
const Filler: React.FC<FillerProps> = ({ height, offset, children }): React.ReactElement => (
<div style={{ height, position: 'relative', overflow: 'hidden' }}>
<div
style={{
marginTop: offset,
position: 'absolute',
left: 0,
right: 0,
top: 0,
display: 'flex',
flexDirection: 'column',
}}
>
{children}
const Filler: React.FC<FillerProps> = ({ height, offset, children }): React.ReactElement => {
let outerStyle: React.CSSProperties = {};

let innerStyle: React.CSSProperties = {
display: 'flex',
flexDirection: 'column',
};

if (offset !== undefined) {
outerStyle = { height, position: 'relative', overflow: 'hidden' };

innerStyle = {
...innerStyle,
marginTop: offset,
position: 'absolute',
left: 0,
right: 0,
top: 0,
};
}

return (
<div style={outerStyle}>
<div style={innerStyle}>{children}</div>
</div>
</div>
);
);
};

export default Filler;
Loading