CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-preact-compat

A React compatibility layer for Preact

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

transition-groups.mddocs/

Transition Groups

Animation and transition components for managing component lifecycle animations and CSS transitions.

Capabilities

Transition Group

Container component that manages entering and leaving transitions for its children.

/**
 * Container for managing child component transitions
 */
class TransitionGroup extends Component {
  /**
   * TransitionGroup props
   */
  props: {
    /** Component type to render as (default: 'span') */
    component?: string | ComponentType;
    
    /** Additional props passed to component */
    [key: string]: any;
  };
}

/**
 * CSS-based transition group with automatic class management
 */
class CSSTransitionGroup extends Component {
  /**
   * CSSTransitionGroup props
   */
  props: {
    /** Base name for CSS classes */
    transitionName: string | TransitionClasses;
    
    /** Enter transition duration in ms */
    transitionEnterTimeout: number;
    
    /** Leave transition duration in ms */
    transitionLeaveTimeout: number;
    
    /** Whether to apply transition on initial mount */
    transitionAppear?: boolean;
    
    /** Appear transition duration in ms */
    transitionAppearTimeout?: number;
    
    /** Component type to render as (default: 'span') */
    component?: string | ComponentType;
    
    /** Additional props passed to component */
    [key: string]: any;
  };
}

/**
 * CSS class names for different transition states
 */
interface TransitionClasses {
  enter: string;
  enterActive: string;
  leave: string;
  leaveActive: string;
  appear?: string;
  appearActive?: string;
}

Usage Examples:

import { CSSTransitionGroup } from 'preact-compat';
// or
import { CSSTransitionGroup } from 'preact-compat/lib/ReactTransitionGroup';

class TodoList extends Component {
  state = {
    todos: [
      { id: 1, text: 'Learn React' },
      { id: 2, text: 'Build an app' }
    ]
  };
  
  addTodo = () => {
    const newTodo = {
      id: Date.now(),
      text: `Todo ${this.state.todos.length + 1}`
    };
    
    this.setState({
      todos: [...this.state.todos, newTodo]
    });
  };
  
  removeTodo = (id) => {
    this.setState({
      todos: this.state.todos.filter(todo => todo.id !== id)
    });
  };
  
  render() {
    return (
      <div>
        <button onClick={this.addTodo}>Add Todo</button>
        
        <CSSTransitionGroup
          transitionName="todo"
          transitionEnterTimeout={300}
          transitionLeaveTimeout={300}
          component="ul"
        >
          {this.state.todos.map(todo => (
            <li key={todo.id}>
              {todo.text}
              <button onClick={() => this.removeTodo(todo.id)}>
                Remove
              </button>
            </li>
          ))}
        </CSSTransitionGroup>
      </div>
    );
  }
}

CSS Classes and Animations

/* CSS for the example above */

/* Enter transition */
.todo-enter {
  opacity: 0;
  transform: translateX(-100%);
}

.todo-enter-active {
  opacity: 1;
  transform: translateX(0);
  transition: all 300ms ease-in;
}

/* Leave transition */
.todo-leave {
  opacity: 1;
  transform: translateX(0);
}

.todo-leave-active {
  opacity: 0;
  transform: translateX(100%);
  transition: all 300ms ease-out;
}

/* Appear transition (optional) */
.todo-appear {
  opacity: 0;
  transform: scale(0.8);
}

.todo-appear-active {
  opacity: 1;
  transform: scale(1);
  transition: all 300ms ease-in-out;
}

Custom Transition Classes

import { CSSTransitionGroup } from 'preact-compat';

const customTransitions = {
  enter: 'slide-in',
  enterActive: 'slide-in-active',
  leave: 'slide-out',  
  leaveActive: 'slide-out-active',
  appear: 'fade-in',
  appearActive: 'fade-in-active'
};

function AnimatedList({ items }) {
  return (
    <CSSTransitionGroup
      transitionName={customTransitions}
      transitionEnterTimeout={500}
      transitionLeaveTimeout={500}
      transitionAppear={true}
      transitionAppearTimeout={500}
      component="div"
      className="animated-container"
    >
      {items.map(item => (
        <div key={item.id} className="animated-item">
          {item.content}
        </div>
      ))}
    </CSSTransitionGroup>
  );
}

Modal with Transitions

import { CSSTransitionGroup } from 'preact-compat';

class Modal extends Component {
  render() {
    const { isOpen, onClose, children } = this.props;
    
    return (
      <CSSTransitionGroup
        transitionName="modal"
        transitionEnterTimeout={200}
        transitionLeaveTimeout={200}
        component="div"
      >
        {isOpen && (
          <div key="modal" className="modal-backdrop" onClick={onClose}>
            <div className="modal-content" onClick={e => e.stopPropagation()}>
              <button className="modal-close" onClick={onClose}>×</button>
              {children}
            </div>
          </div>
        )}
      </CSSTransitionGroup>
    );
  }
}

// CSS for modal transitions
/*
.modal-enter {
  opacity: 0;
}

.modal-enter-active {
  opacity: 1;
  transition: opacity 200ms ease-in;
}

.modal-leave {
  opacity: 1;
}

.modal-leave-active {
  opacity: 0;
  transition: opacity 200ms ease-out;
}

.modal-backdrop {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal-content {
  background: white;
  padding: 20px;
  border-radius: 4px;
  position: relative;
  max-width: 500px;
  width: 90%;
}
*/

Route Transitions

import { CSSTransitionGroup } from 'preact-compat';

class App extends Component {
  state = { currentRoute: 'home' };
  
  navigate = (route) => {
    this.setState({ currentRoute: route });
  };
  
  renderRoute = () => {
    const { currentRoute } = this.state;
    
    switch (currentRoute) {
      case 'home':
        return <HomePage key="home" />;
      case 'about':
        return <AboutPage key="about" />;
      case 'contact':
        return <ContactPage key="contact" />;
      default:
        return <HomePage key="home" />;
    }
  };
  
  render() {
    return (
      <div>
        <nav>
          <button onClick={() => this.navigate('home')}>Home</button>
          <button onClick={() => this.navigate('about')}>About</button>
          <button onClick={() => this.navigate('contact')}>Contact</button>
        </nav>
        
        <CSSTransitionGroup
          transitionName="page"
          transitionEnterTimeout={300}
          transitionLeaveTimeout={300}
          component="main"
          className="page-container"
        >
          {this.renderRoute()}
        </CSSTransitionGroup>
      </div>
    );
  }
}

Basic TransitionGroup

import { TransitionGroup } from 'preact-compat';

class CustomTransitionGroup extends Component {
  render() {
    return (
      <TransitionGroup component="div" className="transition-container">
        {this.props.items.map(item => (
          <CustomTransitionChild key={item.id} item={item} />
        ))}
      </TransitionGroup>
    );
  }
}

// Custom child component that handles its own transitions
class CustomTransitionChild extends Component {
  componentWillEnter(done) {
    // Custom enter animation
    const el = this.refs.element;
    el.style.opacity = '0';
    el.style.transform = 'translateY(-20px)';
    
    requestAnimationFrame(() => {
      el.style.transition = 'all 300ms ease';
      el.style.opacity = '1';
      el.style.transform = 'translateY(0)';
      
      setTimeout(done, 300);
    });
  }
  
  componentWillLeave(done) {
    // Custom leave animation
    const el = this.refs.element;
    el.style.transition = 'all 300ms ease';
    el.style.opacity = '0';
    el.style.transform = 'translateY(20px)';
    
    setTimeout(done, 300);
  }
  
  render() {
    return (
      <div ref="element">
        {this.props.item.content}
      </div>
    );
  }
}

Import Patterns

// Main imports
import { TransitionGroup, CSSTransitionGroup } from 'preact-compat';

// Library imports
import { TransitionGroup, CSSTransitionGroup } from 'preact-compat/lib/ReactTransitionGroup';

// CommonJS
const { TransitionGroup, CSSTransitionGroup } = require('preact-compat/lib/ReactTransitionGroup');

Types

interface TransitionGroupProps {
  component?: string | ComponentType;
  [key: string]: any;
}

interface CSSTransitionGroupProps {
  transitionName: string | TransitionClasses;
  transitionEnterTimeout: number;
  transitionLeaveTimeout: number;
  transitionAppear?: boolean;
  transitionAppearTimeout?: number;
  component?: string | ComponentType;
  [key: string]: any;
}

interface TransitionClasses {
  enter: string;
  enterActive: string;
  leave: string;
  leaveActive: string;
  appear?: string;
  appearActive?: string;
}

interface TransitionChildComponent {
  componentWillEnter?(done: () => void): void;
  componentDidEnter?(): void;
  componentWillLeave?(done: () => void): void;
  componentDidLeave?(): void;
  componentWillAppear?(done: () => void): void;
  componentDidAppear?(): void;
}

Install with Tessl CLI

npx tessl i tessl/npm-preact-compat

docs

children-utilities.md

context-api.md

core-api.md

immutability-helpers.md

index.md

legacy-apis.md

performance-tools.md

refs-system.md

server-rendering.md

transition-groups.md

tile.json