Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions docs/.vuepress/components/UndoExample.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<template>
<div>
<p>Type a text to enable undo and redo</p>
<input v-model="value" />

<div>
<button @click="undo()" :disabled="!prev.length">Undo</button>
<button @click="redo()" :disabled="!next.length">Redo</button>
</div>

<p>
<b>Prev</b>
{{ prev }}
</p>

<p>
<b>Next</b>
{{ next }}
</p>
</div>
</template>

<script>
import { useUndo } from "vue-composable";
export default {
setup() {
return useUndo();
}
};
</script>
6 changes: 6 additions & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ module.exports = {
collapsable: false,
children: [["composable/meta/title", "Title"]]
},
{
title: "state",
sidebarDepth: 1,
collapsable: false,
children: [["composable/state/undo", "Undo"]]
},
{
title: "External",
sidebarDepth: 1,
Expand Down
4 changes: 4 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ Check out the [examples folder](examples) or start hacking on [codesandbox](http

- [Title](https://pikax.me/vue-composable/composable/meta/title) - reactive `document.title`

### State

- [Undo](https://pikax.me/vue-composable/composable/state/undo) - Tracks variable history, to allow `undo` and `redo`

### Web

- [Fetch](https://pikax.me/vue-composable/composable/web/fetch) - reactive `fetch` wrapper
Expand Down
37 changes: 37 additions & 0 deletions docs/api/vue-composable.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,9 @@ export interface LocalStorageReturn<T> {
// @public (undocumented)
export type LocalStorageTyped<T extends object> = string;

// @public (undocumented)
export const MAX_ARRAY_SIZE: number;

// @public (undocumented)
export function minMax(val: number, min: number, max: number): number;

Expand Down Expand Up @@ -896,6 +899,31 @@ export interface StorageSerializer<T = any> {
stringify(item: T): string;
}

// @public (undocumented)
export interface UndoOperation {
(step: number): void;
(): void;
}

// @public (undocumented)
export interface UndoOptions<T> {
clone: (entry: T) => T;
deep: boolean;
maxLength: number;
}

// @public (undocumented)
export interface UndoReturn<T> {
jump(delta: number): void;
next: ComputedRef<T[]>;
prev: ComputedRef<T[]>;
redo(): void;
redo(step: number): void;
undo(): void;
undo(step: number): void;
value: Ref<T>;
}

// @public (undocumented)
export function unwrap<T>(o: RefTyped<T>): T;

Expand Down Expand Up @@ -1617,6 +1645,15 @@ export function useStorage<T extends object = any>(
// @public (undocumented)
export function useTitle(overrideTitle?: string | null): Ref<string | null>;

// @public (undocumented)
export function useUndo<T = any>(): UndoReturn<T | undefined>;

// @public (undocumented)
export function useUndo<T>(
defaultValue: RefTyped<T>,
options?: Partial<UndoOptions<T>>
): UndoReturn<T>;

// Warning: (ae-forgotten-export) The symbol "UseValidation" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "ValidationOutput" needs to be exported by the entry point index.d.ts
// Warning: (ae-forgotten-export) The symbol "ValidationGroupResult" needs to be exported by the entry point index.d.ts
Expand Down
118 changes: 118 additions & 0 deletions docs/composable/state/undo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Undo

> Tracks variable history, to allow `undo` and `redo`

## Parameters

```js
import { useUndo } from "vue-composable";

export interface UndoOptions<T> {
/**
* Watch `deep` option for changes
*/
deep: boolean;

/**
* Max history change
* @default MAX_ARRAY_SIZE
*/
maxLength: number;

/**
* Clone strategy
* @default (x)=>x
*/
clone: (entry: T) => T;
}

const defaultOptions = {
deep: undefined,


maxLength: MAX_ARRAY_SIZE,

clone(x) {
return x;
}
}


useUndo(defaultValue?, options?);
```

| Parameters | Type | Required | Default | Description |
| ------------ | ----------- | -------- | ---------------- | --------------------- |
| defaultValue | `Ref<T>|T` | `false` | `undefined` | Default value |
| options | `(x: T)=>T` | `false` | `defaultOptions` | Configuration options |

## State

The `useUndo` function exposes the following reactive state:

```js
import { useUndo } from "vue-composable";

const { value, prev, next } = useUndo();
```

| State | Type | Description |
| ----- | ---------- | ------------------------------------------ |
| value | `Ref<T>` | State value |
| prev | `Ref<T[]>` | Array of prev states |
| next | `Ref<T[]>` | Array of next states, only if you `undo()` |

## Methods

The `useUndo` function exposes the following methods:

```js
import { useUndo } from "vue-composable";

const { jump, undo, redo } = useUndo();
```

| Signature | Description |
| ------------- | -------------------------------------------------------------------------------------------- |
| `jump(delta)` | moves the cursor to `delta`, if delta is positive it will `undo`, if negative it will `redo` |
| `undo(n?)` | Undo the state to `n` default to 1 |
| `redo(n?)` | Redo the state to `n` default to 1 |

## Example

<undo-example/>

### Code

```vue
<template>
<div>
<p>Type a text to enable undo and redo</p>
<input v-model="value" />

<div>
<button @click="undo()" :disabled="!prev.length">Undo</button>
<button @click="redo()" :disabled="!next.length">Redo</button>
</div>

<p>
<b>Prev</b>
{{ prev }}
</p>

<p>
<b>Next</b>
{{ next }}
</p>
</div>
</template>

<script>
import { useUndo } from "vue-composable";
export default {
setup() {
return useUndo();
}
};
</script>
```
4 changes: 4 additions & 0 deletions packages/vue-composable/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ Check our [documentation](https://pikax.me/vue-composable/)

- [Title](https://pikax.me/vue-composable/composable/meta/title) - reactive `document.title`

### State

- [Undo](https://pikax.me/vue-composable/composable/state/undo) - Tracks variable history, to allow `undo` and `redo`

### Web

- [Fetch](https://pikax.me/vue-composable/composable/web/fetch) - reactive `fetch` wrapper
Expand Down
68 changes: 68 additions & 0 deletions packages/vue-composable/__tests__/state/undo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { useUndo } from "../../src";
import { ref } from "../../src/api";
describe("undo", () => {
it("should work", () => {
const v = ref(0);

const undo = useUndo(v);

expect(undo.value).toBe(v);

v.value = 1;
expect(undo.prev.value).toHaveLength(1);

v.value++;
expect(undo.prev.value).toHaveLength(2);

undo.undo();
expect(v.value).toBe(1);
expect(undo.prev.value).toHaveLength(2);
expect(undo.next.value).toHaveLength(1);

undo.redo();
expect(undo.prev.value).toHaveLength(2);
expect(undo.next.value).toHaveLength(0);

const x = 10;
for (let i = 0; i < x; ++i) {
v.value = i;
}
expect(undo.prev.value).toHaveLength(2 + x);
expect(undo.next.value).toHaveLength(0);

undo.jump(x);

expect(undo.prev.value).toHaveLength(3);
expect(undo.next.value).toHaveLength(x);

v.value = 42;
expect(undo.prev.value).toHaveLength(3);
expect(undo.next.value).toHaveLength(0);
});

it("should only store maxItems", () => {
const undo = useUndo(1, { maxLength: 2 });

undo.value.value++;
expect(undo.prev.value).toStrictEqual([1]);

undo.value.value++;
expect(undo.prev.value).toStrictEqual([2, 1]);

undo.value.value++;
expect(undo.prev.value).toStrictEqual([3, 2]);

undo.value.value++;
expect(undo.prev.value).toStrictEqual([4, 3]);
});

it("should use clone function", () => {
const clone = jest.fn() as any;
const undo = useUndo({ a: 1 }, { clone });

expect(clone).toHaveBeenCalled();

undo.value.value = { a: 2 };
expect(clone).toHaveBeenCalledTimes(2);
});
});
2 changes: 2 additions & 0 deletions packages/vue-composable/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ export * from "./validation";
export * from "./i18n";
export * from "./meta";
export * from "./ssr";
export * from "./state";

export const VERSION = __VERSION__;
// istanbul ignore next
export const VUE_VERSION: "2" | "3" = __VUE_2__ ? "2" : "3";
export const COMMIT = __COMMIT__;
1 change: 1 addition & 0 deletions packages/vue-composable/src/state/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./undo";
Loading