React lifecycle controlled motion library for smooth enter/leave transitions and list animations
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Specialized component for animating arrays of keyed elements with smooth add/remove/reorder transitions. CSSMotionList automatically handles element lifecycle, position changes, and provides advanced diffing algorithms for optimal performance.
Advanced list animation component that tracks keyed elements and applies appropriate enter/leave animations as the list changes.
/**
* List animation component for arrays of keyed elements
* @param props - Configuration extending CSSMotionProps with list-specific options
* @returns React component that renders animated list
*/
export function CSSMotionList(props: CSSMotionListProps): React.ReactElement;
interface CSSMotionListProps
extends Omit<CSSMotionProps, 'onVisibleChanged' | 'children'>,
Omit<React.HTMLAttributes<any>, 'children'> {
/** Array of React keys or key objects for list items */
keys: (React.Key | { key: React.Key; [name: string]: any })[];
/** Wrapper component type - string, component, or false for no wrapper */
component?: string | React.ComponentType | false;
/** Called when individual item visibility changes */
onVisibleChanged?: (visible: boolean, info: { key: React.Key }) => void;
/** Called when all items have been removed from the list */
onAllRemoved?: () => void;
/** Render function for each list item */
children?: (
props: {
visible?: boolean;
className?: string;
style?: React.CSSProperties;
index?: number;
[key: string]: any;
},
ref: (node: any) => void,
) => React.ReactElement;
}Usage Examples:
import { CSSMotionList } from "rc-motion";
// Basic animated list
function AnimatedList() {
const [items, setItems] = useState(['a', 'b', 'c']);
return (
<CSSMotionList
keys={items}
motionName="list-item"
component="ul"
>
{({ key, style, className }) => (
<li key={key} style={style} className={className}>
Item {key}
</li>
)}
</CSSMotionList>
);
}
// Advanced list with callbacks
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Buy milk', completed: false },
{ id: 2, text: 'Walk dog', completed: true },
]);
return (
<CSSMotionList
keys={todos.map(todo => ({ key: todo.id, ...todo }))}
motionName="todo"
component="div"
onVisibleChanged={(visible, { key }) => {
console.log(`Todo ${key} visibility: ${visible}`);
}}
onAllRemoved={() => {
console.log('All todos removed');
}}
>
{({ key, style, className, ...props }) => (
<div key={key} style={style} className={className}>
<input
type="checkbox"
checked={props.completed}
onChange={() => toggleTodo(props.id)}
/>
<span>{props.text}</span>
<button onClick={() => removeTodo(props.id)}>Delete</button>
</div>
)}
</CSSMotionList>
);
}System for tracking and diffing list items to determine appropriate animations.
/**
* Key object interface for list items with optional metadata
*/
interface KeyObject {
key: string | number;
status?: DiffStatus;
[property: string]: any;
}
/**
* Status types for list item changes
*/
type DiffStatus = 'add' | 'keep' | 'remove' | 'removed';
/**
* Convert key or key object to normalized KeyObject format
* @param key - React key or key object
* @returns Normalized key object
*/
function wrapKeyToObject(key: React.Key | KeyObject): KeyObject;
/**
* Parse array of keys to KeyObject array
* @param keys - Array of keys to parse
* @returns Array of normalized key objects
*/
function parseKeys(keys: any[]): KeyObject[];
/**
* Compare previous and current keys to determine changes
* @param prevKeys - Previous key objects
* @param currentKeys - Current key objects
* @returns Array of key objects with diff status
*/
function diffKeys(
prevKeys: KeyObject[],
currentKeys: KeyObject[],
): KeyObject[];Internal state interface for tracking list changes and animations.
interface CSSMotionListState {
keyEntities: KeyObject[];
}
/**
* Generate CSSMotionList component with custom CSSMotion
* @param transitionSupport - Browser transition support flag
* @param CSSMotion - Custom CSSMotion component
* @returns Configured CSSMotionList class component
*/
function genCSSMotionList(
transitionSupport: boolean,
CSSMotion?: React.ComponentType<CSSMotionProps>,
): React.ComponentClass<CSSMotionListProps>;CSSMotionList inherits most properties from CSSMotionProps, enabling consistent animation behavior across single elements and lists.
// Inherited from CSSMotionProps
interface InheritedMotionProps {
motionName?: MotionName;
motionAppear?: boolean;
motionEnter?: boolean;
motionLeave?: boolean;
motionLeaveImmediately?: boolean;
motionDeadline?: number;
removeOnLeave?: boolean;
leavedClassName?: string;
// Event handlers apply to each list item
onAppearPrepare?: MotionPrepareEventHandler;
onEnterPrepare?: MotionPrepareEventHandler;
onLeavePrepare?: MotionPrepareEventHandler;
onAppearStart?: MotionEventHandler;
onEnterStart?: MotionEventHandler;
onLeaveStart?: MotionEventHandler;
onAppearActive?: MotionEventHandler;
onEnterActive?: MotionEventHandler;
onLeaveActive?: MotionEventHandler;
onAppearEnd?: MotionEndEventHandler;
onEnterEnd?: MotionEndEventHandler;
onLeaveEnd?: MotionEndEventHandler;
}Complex use cases and patterns for list animations.
// Staggered animations
function StaggeredList() {
return (
<CSSMotionList
keys={items}
motionName="stagger"
motionDeadline={1000}
onEnterStart={(element, event) => {
const index = Array.from(element.parentNode.children).indexOf(element);
return {
animationDelay: `${index * 100}ms`,
opacity: 0,
transform: 'translateY(-20px)'
};
}}
onEnterActive={(element) => ({
opacity: 1,
transform: 'translateY(0)'
})}
>
{({ key, style, className, index }) => (
<div key={key} style={style} className={className}>
Staggered item {index}
</div>
)}
</CSSMotionList>
);
}
// Custom wrapper component
function CustomList() {
return (
<CSSMotionList
keys={items}
motionName="custom"
component={({ children, ...props }) => (
<div {...props} className="custom-list-wrapper">
<h2>Animated Items</h2>
{children}
</div>
)}
>
{({ key, style, className }) => (
<div key={key} style={style} className={className}>
Custom wrapped item
</div>
)}
</CSSMotionList>
);
}Install with Tessl CLI
npx tessl i tessl/npm-rc-motion