All Projects β†’ charkour β†’ zundo

charkour / zundo

Licence: MIT license
🍜 undo/redo middleware for zustand

Programming Languages

typescript
32286 projects
javascript
184084 projects - #8 most used programming language
shell
77523 projects

Projects that are alternatives of or similar to zundo

actions
Software without side-effects. Redo and Undo.
Stars: ✭ 23 (-86.47%)
Mutual labels:  redo, undo
js-undo-manager
Simple JavaScript undo/redo command manager supporting transactions with no dependencies
Stars: ✭ 23 (-86.47%)
Mutual labels:  redo, undo
undo
A undo-redo library.
Stars: ✭ 38 (-77.65%)
Mutual labels:  redo, undo
undox
⎌ Redux Implementation of Undo/Redo based on storing actions instead of states.
Stars: ✭ 25 (-85.29%)
Mutual labels:  redo, undo
rundo
Rundo is a undo redo library for rust which can auto generate undo op
Stars: ✭ 32 (-81.18%)
Mutual labels:  redo, undo
Regret
[Moved to MavenCentral] An undo-redo Android library which works with any objects and with an easy implementation. Perfect for drawing, text and photo editing apps.
Stars: ✭ 65 (-61.76%)
Mutual labels:  redo, undo
Redux Undo
♻️ higher order reducer to add undo/redo functionality to redux state containers
Stars: ✭ 2,744 (+1514.12%)
Mutual labels:  redo, undo
UndoRedo.js
A powerful and simple JavaScript library provides a history for undo/redo functionality. Just like a time machine! πŸ•
Stars: ✭ 19 (-88.82%)
Mutual labels:  redo, undo
sorting-visualizer
Sorting Algorithms Visualizer
Stars: ✭ 429 (+152.35%)
Mutual labels:  zustand
react-native-template
An opinionated template to bootstrap your next React Native app with all the time-wasting packages you need to have.
Stars: ✭ 132 (-22.35%)
Mutual labels:  zustand
undo
GelΓΆschte Artikel, Slices und Kategorien wiederherstellen
Stars: ✭ 31 (-81.76%)
Mutual labels:  undo
Flexibleadapter
Fast and versatile Adapter for RecyclerView which regroups several features into one library to considerably improve the user experience :-)
Stars: ✭ 3,482 (+1948.24%)
Mutual labels:  undo
MagicWE2
[MagicWE2] Lag free asynchronous world editor for PMMP with plenty of options
Stars: ✭ 109 (-35.88%)
Mutual labels:  undo
expansion-pack
πŸ”‹ Useful stack expansion for ts-nextjs-tailwind-starter
Stars: ✭ 16 (-90.59%)
Mutual labels:  zustand
zoov
Use 🐻 Zustand with Module-like api
Stars: ✭ 24 (-85.88%)
Mutual labels:  zustand
minesweeper
πŸ’£Minesweeper game written with React
Stars: ✭ 18 (-89.41%)
Mutual labels:  zustand
zustood
πŸ»β€β„οΈ A modular store factory using zustand
Stars: ✭ 46 (-72.94%)
Mutual labels:  zustand
libgen-downloader
A simple tool to search and download ebooks from libgen via terminal user interface.
Stars: ✭ 98 (-42.35%)
Mutual labels:  zustand
solid-zustand
🐻 State management in Solid using zustand.
Stars: ✭ 44 (-74.12%)
Mutual labels:  zustand

🍜 Zundo

zundo v2 is on it's way! Checkout the progess here.

enable time-travel in your apps. undo/redo middleware for zustand. built with zustand.

Build Size Version Downloads

zundo demo

See a demo

Install

npm i zustand zundo

First create a store with undo middleware

This returns the familiar store accessible by a hook! But now your store tracks past actions.

import create, { UndoState } from 'zundo';

// define the store (typescript)
interface StoreState extends UndoState {
  bears: number;
  increasePopulation: () => void;
  removeAllBears: () => void;
}

// creates a store with undo/redo capability
const useStoreWithUndo = create<StoreState>((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
}));

Then bind your components

Use your store anywhere, including undo, redo, and clear!

const App = () => {
  const { bears, increasePopulation, removeAllBears, undo, redo, clear } =
    useStoreWithUndo();

  return (
    <>
      bears: {bears}
      <button onClick={increasePopulation}>increase</button>
      <button onClick={removeAllBears}>remove</button>
      <button onClick={undo}>undo</button>
      <button onClick={redo}>redo</button>
      <button onClick={clear}>clear</button>
    </>
  );
};

Alternatively, use the middleware

Instead of using create from zundo, use the zundo middleware and the zustand create.

import { undoMiddleware, UndoState } from 'zundo';
import create from 'zustand';

const useStoreWithUndo = create<StoreState>(
  undoMiddleware((set) => ({
    bears: 0,
    increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
    removeAllBears: () => set({ bears: 0 }),
  })),
);

Middleware Options

options: {
  exclude?: string[],
  include?: string[],
  allowUnchanged?: boolean,
  historyDepthLimit?: number,
  coolOffDurationMs?: number,
}

Exclude fields from being tracked in history

Use the exclude option, which accepts an array of keys to not track. Alternatively you can use the include option which will result in only those keys being tracked.

If for some reason you use both parameters, any key included in both will be excluded.

// Only field1 and field2 will be tracked
const useStoreA = create<StoreState>(
  undoMiddleware(
    set => ({ ... }),
    { include: ['field1', 'field2'] }
  )
);

// Everything besides field1 and field2 will be tracked
const useStoreB = create<StoreState>(
  undoMiddleware(
    set => ({ ... }),
    { exclude: ['field1', 'field2'] }
  )
);

Note: exclude replaces the option omit which will deprecated in future versions.

Allow unchanged states to be stored

Sometimes you may want to track states in history even if nothing has actually changed. Set allowUnchanged to be true to allow unchanged states to be stored. By default, it is false (well, technically it is undefined).

const useStore = create<StoreState>(
  undoMiddleware(
    set => ({ ... }),
    { allowUnchanged: true }
  )
);

Limit number of states stored

For performance reasons, you may want to limit the number of previous and future states stored in history. Setting historyDepthLimit will limit the number of previous and future states stored in the zundo store. By default, no limit is set.

const useStore = create<StoreState>(
  undoMiddleware(
    set => ({ ... }),
    { historyDepthLimit: 100 }
  )
);

API

undoMiddleware(config: StateCreator<TState>)

This is middleware for zustand which takes in a config for the store.

This works for multiple undoable stores in the same app.

create

Create from zundo will return a store hook that has undo/redo capabilities. In addition to what fields are in the provided in your StoreState, the functions undo, redo, clear, and getState are added as well.

This works for multiple undoable stores in the same app.

  • undo: call function to apply previous state (if there are previous states)
  • redo: call function to apply future state (if there are future states). Future states are "previous previous states."
  • clear: call function to remove all stored states from your undo store. Warning: clearing cannot be undone.

Dispatching a new state will clear all of the future states.

createUndoStore()

Will create a store that is used by the middleware to track the internal state of type UndoStoreState.

UndoState

A type to extend when creating a global store with undo/redo capabilities.

type UndoState = {
  // Will go back one state
  undo?: (() => void) | undefined;
  // Will go forward one state
  redo?: (() => void) | undefined;
  // Will clear
  clear?: (() => void) | undefined;
  getState?: (() => UndoStoreState) | undefined;
  // history is enabled by default
  setIsUndoHistoryEnabled?: ((isEnabled: boolean) => void) | undefined;
};

Enable or Disable History

Sometimes you may want to disable storing states in history, for example, when animating a point across the screen and you only want to store the beginning and end points in history. Use setIsUndoHistoryEnabled, returned from useStore to set programmatically set true or false.

Usage

import create, { UndoState } from 'zundo';

interface StoreState extends UndoState {
  // fields
}

const useStoreWithUndo = create<StoreState>();
// (set, get, api)

Cool-off period

Sometimes multiple state changes might happen in a short amount of time and you only want to store one change in history. Use coolOffDurationMs to set how long (in millesconds) to stop new changes from being stored in history after an initial change has happened.

Please note: Even if you don't set coolOffDurationMs, if multiple state changes happen inside of the same frame (e.g. you called setSomething multiple times in the same function call), only one item will be stored in history.

UseStore

It is an interface from zustand where T is your StoreState. Very similar to the type definition shown below. It is the type of any useStore hook. Used when passing the useStore hook as a prop.

type UseStore<T extends object> = {
    (): T;
    <U>(selector: StateSelector<T, U>, equalityFn?: EqualityChecker<U> | undefined): U;
    setState: SetState<T>;
    getState: GetState<...>;
    subscribe: Subscribe<...>;
    destroy: Destroy;
}

UndoStoreState

An interface for the store that tracks states.

type UndoStoreState = {
  prevStates: any[];
  futureStates: any[];
  undo: () => void;
  redo: () => void;
  clear: () => void;
  setStore: Function;
  getStore: Function;
};

Road Map

  • possibly use better data structure for storing previous states. Maybe just a diff between states?

Contributing

pnpm is used as a package manager. Run pnpm install to install local dependencies.

Issues and PRs are welcome. I'd like to hear your comments and critiques. We can discuss ways to make this package better. Thanks :)

Author

Versioning

View the releases for the change log. This generally follows sem-ver, but breaking changes may occur in v1.

Publish using np --no-cleanup.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].