CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-rc-motion

React lifecycle controlled motion library for smooth enter/leave transitions and list animations

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

css-motion-list.mddocs/

List Animation

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.

Capabilities

CSSMotionList Component

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>
  );
}

Key Management

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[];

List State Management

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>;

Motion Properties Inheritance

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;
}

Advanced List Scenarios

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

docs

css-motion-list.md

css-motion.md

index.md

provider.md

tile.json