Skip to content

Handle and dispatch transition states from anywhere in the application.

License

Notifications You must be signed in to change notification settings

cher-ami/transitions-manager

Repository files navigation

Transitions manager

npm build build

Transitions manager allows to handle and dispatch transition states from anywhere in the application.

demo

Summary

Installation

npm i @cher-ami/transitions-manager

Life cycle

TransitionsManager allows to handle and dispatch two types of states:

  • mountState mount | unmount
  • playState hidden | play-in | visible | play-out

The life cycle of a single transition order could be:

  • mount (mountState)
  • play-in (playState)
  • visible (playState)
  • play-out (playState)
  • hidden (playState) - default
  • unmount (mountState) - default

It's possible to manage a transitions without using the mountState and only working with playState. The mountState is useful specificaly about the React usage.

Vanilla usage

Create a new instance to manager the transitions of your component:

export const componentTransitionsManager = new TransitionsManager()

Listen mountState change:

const handleMountState = (mountState) => {
  if (mountState === "mount") {
    // do somthing and resolve mountState
    componentTransitionsManager.mountComplete()
  }
  if (mountState === "unmount") {
    // do somthing and resolve mountState
    componentTransitionsManager.unmountComplete()
  }
}
// start to listen
componentTransitionsManager.mountStateSignal.on(handleMountState)
// stop...
componentTransitionsManager.mountStateSignal.off(handleMountState)

Listen playState change on the same way:

const handlePlayState = (playState) => {
  if (playState === "play-in") {
    // do something and resolve transition state
    componentTransitionsManager.playInComplete()
  }
  if (playState === "play-out") {
    // do something and resolve transition state
    componentTransitionsManager.playOutComplete()
  }
}
// start to listen
componentTransitionsManager.playStateSignal.on(handlePlayState)
// stop...
componentTransitionsManager.playStateSignal.off(handlePlayState)

Now from anywhere, dispatch a new transition state with these methods

componentTransitionsManager.mount()
componentTransitionsManager.playIn()
componentTransitionsManager.playOut()
componentTransitionsManager.unmount()

Options parameters

playIn and playOut methods accept options parameters.

componentTransitionsManager.playIn({ duration: 0 })

From the components:

componentTransitionsManager.playStateSignal.on((playState, options) => {
  console.log(options) // { duration: 0 }
})

Default options can be set on the manager instance:

const componentTransitionsManager = new TransitionsManager({ 
  options: {
    duration: 1
  } 
})

For typescript developers, GOption generic type is available on instance

const componentTransitionsManager = new TransitionsManager<{duration?: number}>({ 
  options: {
    duration: 1
  } 
})

React usage

TransitionsManager is ready to use with vanilla javascript as above but it has been built for a React usage too. The API comes with hooks!

usePlayIn & usePlayOut

  1. Create a new transitionsManager instance as above
  2. Wrap your component by TransitionsHoc(component, manager)
  3. Then, define transitions in usePlayIn and usePlayOut hooks callback.
export const componentTransitionsManager = new TransitionsManager()
function Component() {
  usePlayIn(componentTransitionsManager, async (done, options) => {
    await myPlayIn()
    done()
  })
  usePlayOut(componentTransitionsManager, async (done, options) => {
    await myPlayOut()
    done()
  })
  return <div>...</div>
}

export default TransitionsHoc(Component, componentTransitionsManager)

Now, from anywhere in the application, you can play the component via componentTransitionsManager his own transitionsManager instance.

await componentTransitionsManager.playIn()
// now, the transtion is done.

componentTransitionsManager.playIn() will execute the transition function of usePlayIn hook defined previously in Component. This method returns a promise that will be resolved when the transition is done with done() function from the same hook. Of course, "awaiting" the promise is not mandatory.

The TransitionsHoc function will mount and unmount automatically the component before play out and after play out. It's possible to only play in and play out without destroy the component with autoMountUnmount option:

const componentTransitionsManager = new TransitionsManager({ autoMountUnmount: false })

useTransitionsManager

Instead of handle the transitionsManager playState with usePlayIn and usePlayOut hooks, you can use the useTransitionsManager hook in your component.

This one returns the current playState of the transitionsManager instance when it changes. In this case, you have to execute the playInComplete and playOutComplete functions when the transition is done.

useTransitionsManager(componentTransitionsManager, async (playState, options) => {
  if (playState === "play-in") {
    await myPlayIn()
    componentTransitionsManager.playInComplete()
  }
  if (playState === "play-out") {
    await myPlayOut()
    componentTransitionsManager.playOutComplete()
  }
})

// or get state from useTransitionsManager hook
const {playState, options} = useTransitionsManager(componentTransitionsManager)
// ...

Mount and unmount manually (old API)

If TransitionsHoc wrapper is not used, the mount and unmount component state can be managed manually. By using useIsMount hook from the parent component, you can check the mount and unmount boolean state to condition the rendering.

const App = () => {
  const mountComponent = useIsMount(componentTransitionsManager)
  return <div>{mountComponent && <Component/>}</div>
}

Now, you can mount and unmount the component.

playIn method will call mount methods before is execution, and playOut will call unmount methods after is execution automatically.

await componentTransitionsManager.playIn() // auto mount + playIn
// ...
await componentTransitionsManager.playOut() // playOut + auto unmount

If the autoMountUnmount option is disable, you will have to mount and unmount manually the component as below:

await componentTransitionsManager.mount()
await componentTransitionsManager.playIn()
// ...
await componentTransitionsManager.playOut()
await componentTransitionsManager.unmount()

Debug

@wbe/debug is used on this project. It allows to easily get logs information on development and production modes.

  • To use it, add this line in your browser console:
localStorage.debug = "TransitionsManager:*"
  • Optionally, pass a name parameter to the instance to print it in the debug namespace.
const componentTransitionsManager = new TransitionsManager({name: "Component"})

API

TransitionsManager constructor

{ name?: string, autoMountUnmount?: boolean, options?: Record<any, any> }

  • name (optional) used by debug as namespace
  • autoMountUnmount (optional) playIn will auto mount, playOut will auto unmount - default is true
  • options (optional) list of default playIn and playOut options

Mount

mount(): Promise<void>

mountComplete(): void

Unmount

unmount(): Promise<void>

unmountComplete(): void

PlayIn

playIn(options?: Partials<string, any>): Promise<void>

playInComplete(): void

PlayOut

playOut(options?: Partials<string, any>): Promise<void>

playOutComplete(): void

Utils

stagger(delay: number = 1, anims: (()=> any)[]): [promise: () => Promise<any>, cancel: () => void]

In some case, you want to execute a list of transitions in a staggered way. Staggered transition can be setted with the util stagger function.

import {stagger} from "@cher-ami/transitions-manager"

const [start, clear] = stagger(0.1, [
  componentTransitionsManager.playIn,
  FooterTransitionsManager.playIn,
])

// start staggered transition
await start()

// clear staggered transition if needed
clear()

pnpm i

Start dev server for specific example

pnpm run dev:basic

Start build:watch on the lib witch is symlinked to the example

pnpm run build:watch

Start unit tests watch during development

pnpm run test:watch

Licence

MIT

Credits

Willy Brauner