An open API service indexing awesome lists of open source software.

https://github.com/elite174/solid-undo-redo

A list-based (O(1)) implementation of undo-redo for signals!
https://github.com/elite174/solid-undo-redo

history redo solidjs time-travel undo undo-redo

Last synced: 6 months ago
JSON representation

A list-based (O(1)) implementation of undo-redo for signals!

Awesome Lists containing this project

README

          

# solid-undo-redo

[![version](https://img.shields.io/npm/v/solid-undo-redo?style=for-the-badge)](https://www.npmjs.com/package/solid-undo-redo)
![npm](https://img.shields.io/npm/dw/solid-undo-redo?style=for-the-badge)

A small library for undo-redo operations!
Make signals with history.
The implementation is list-based, so it works in **O(1)** instead of O(n)!

## Features

- Make `undo` or `redo` operations
- Set your history length
- React on undo and redo events with callbacks
- Turn your history into reactive iterator

## Installation

`npm i solid-undo-redo`

`pnpm add solid-undo-redo`

`yarn add solid-undo-redo`

## Usage

```tsx
import { createSignalWithHistory } from "solid-undo-redo";

const [value, setValue, history] = createSignalWithHistory(
undefined,
{ historyLength: 10 }
);

setValue(1);

// Since we haven't provided the initial value (undefined)
// the history length equals to 1
console.assert(history.size() === 1);

// Since the history length is 1
// we can't do undo and redo
console.assert(history.isUndoPossible() === false);
console.assert(history.isRedoPossible() === false);

setValue(2);

// We added new value to the history
console.assert(history.size() === 2);

// Now we can make undo operation
console.assert(history.isUndoPossible() === true);
// However we can't do redo because we're at the end
// of our history
console.assert(history.isRedoPossible() === false);

// let's add some undo/redo listeners
history.registerCallback("undo", (currentValue, previousValue) =>
console.log(`Undo: ${currentValue}, ${previousValue}`)
);

history.registerCallback("redo", (currentValue, previousValue) => {
console.log(`Redo: ${currentValue}, ${previousValue}`);
});

// Don't forget to remove these listeners with api.removeCallback
// when you don't need them!

history.undo();
// You'll see in the console "Undo: 1, 2"

// Now we can't make undo operations
// because we're at the beginning of our history
console.assert(history.isUndoPossible() === false);
// But we can do redo!
console.assert(history.isRedoPossible() === true);

history.redo();
// You'll see in the console "Redo: 2, 1"

// Let's have a look at our history
console.log(history.toArray()); // [1, 2]

// You can also use history.arraySignal
// To get a reactive accessor of the history array

// Now let's clear our history and callbacks
history.dispose();
```

## Docs

```tsx
function createSignalWithHistory(
initialValue?: T,
options?: SignalWithHistoryOptions
): SignalWithHistory;

type CallbackTypeMap = {
undo: (currentValue: T, previousValue: T) => void;
redo: (currentValue: T, previousValue: T) => void;
};

export interface SignalWithHistoryOptions {
/**
* Max history length
* @default 100
*/
historyLength?: number;
/**
* Solid signal options
*/
signalOptions?: SignalOptions | undefined;
}

export type History = {
undo: VoidFunction;
redo: VoidFunction;

/**
* Cleas the history
* @param clearCurrentValue - clears current value if set to true
* @default false
*/
clear: (clearCurrentValue?: boolean) => void;

/** Reactive signal which indicates if undo operation is possible */
isUndoPossible: Accessor;
/** Reactive signal which indicates if redo operation is possible */
isRedoPossible: Accessor;

/**
* Reactive generator function which is retriggered
* when the history changes
*/
createHistoryIterator: () => Generator;

/**
* Reactive signal which shows the current history length
*/
size: Accessor;

/** Register callback for undo/redo */
registerCallback: >(
type: CallbackType,
listener: CallbackTypeMap[CallbackType]
) => void;

/** Remove callback for undo/redo */
removeCallback: >(
type: CallbackType,
listener: CallbackTypeMap[CallbackType]
) => void;

/** Returns non-reactive history array */
toArray: () => Array;

/** Reactive signal of history array */
arraySignal: Accessor>;

/** Clear all registered callbacks and history */
dispose: VoidFunction;
};

export type SignalWithHistory = [
/** Reactive accessor for the value */
get: Accessor,
/** Setter function for the value */
set: Setter,
history: History
];
```