diff --git a/packages/history-utility/docs/modules.md b/packages/history-utility/docs/modules.md index 3e2811a..c615d6b 100644 --- a/packages/history-utility/docs/modules.md +++ b/packages/history-utility/docs/modules.md @@ -36,7 +36,7 @@ #### Defined in -[packages/history-utility/src/history-utility.ts:26](https://github.com/valtiojs/valtio-history/blob/43e2fcd/packages/history-utility/src/history-utility.ts#L26) +[packages/history-utility/src/history-utility.ts:26](https://github.com/valtiojs/valtio-history/blob/3f12f1b/packages/history-utility/src/history-utility.ts#L26) --- @@ -60,23 +60,30 @@ #### Defined in -[packages/history-utility/src/history-utility.ts:10](https://github.com/valtiojs/valtio-history/blob/43e2fcd/packages/history-utility/src/history-utility.ts#L10) +[packages/history-utility/src/history-utility.ts:10](https://github.com/valtiojs/valtio-history/blob/3f12f1b/packages/history-utility/src/history-utility.ts#L10) --- ### HistoryOptions -Ƭ **HistoryOptions**: `Object` +Ƭ **HistoryOptions**\<`T`\>: `Object` + +#### Type parameters + +| Name | +| :--- | +| `T` | #### Type declaration -| Name | Type | Description | -| :--------------- | :-------- | :---------------------------------------------------------------- | -| `skipSubscribe?` | `boolean` | determines if the internal subscribe behaviour should be skipped. | +| Name | Type | Description | +| :--------------- | :-------------------------------------------------------------------------------- | :----------------------------------------------------------------- | +| `skipSubscribe?` | `boolean` | determines if the internal subscribe behaviour should be skipped. | +| `onChange?` | (`value`: `T`, `data`: \{ `historySaved`: `boolean` ; `ops`: `any`[] }) => `void` | callback that triggers any time a change happens wihin the utility | #### Defined in -[packages/history-utility/src/history-utility.ts:43](https://github.com/valtiojs/valtio-history/blob/43e2fcd/packages/history-utility/src/history-utility.ts#L43) +[packages/history-utility/src/history-utility.ts:43](https://github.com/valtiojs/valtio-history/blob/3f12f1b/packages/history-utility/src/history-utility.ts#L43) ## Functions @@ -114,10 +121,10 @@ Notes:
#### Parameters -| Name | Type | Description | -| :------------- | :--------------------------------------------------------- | :--------------------------------------------- | -| `initialValue` | `V` | any value to be tracked | -| `options?` | `boolean` \| [`HistoryOptions`](modules.md#historyoptions) | use to configure the proxyWithHistory utility. | +| Name | Type | Description | +| :------------- | :---------------------------------------------------------------- | :--------------------------------------------- | +| `initialValue` | `V` | any value to be tracked | +| `options?` | `boolean` \| [`HistoryOptions`](modules.md#historyoptions)\<`V`\> | use to configure the proxyWithHistory utility. | #### Returns @@ -154,4 +161,4 @@ const state = proxyWithHistory({ #### Defined in -[packages/history-utility/src/history-utility.ts:129](https://github.com/valtiojs/valtio-history/blob/43e2fcd/packages/history-utility/src/history-utility.ts#L129) +[packages/history-utility/src/history-utility.ts:136](https://github.com/valtiojs/valtio-history/blob/3f12f1b/packages/history-utility/src/history-utility.ts#L136) diff --git a/packages/history-utility/src/__tests__/history-utility.vanilla.spec.ts b/packages/history-utility/src/__tests__/history-utility.vanilla.spec.ts index 909526c..fd09ade 100644 --- a/packages/history-utility/src/__tests__/history-utility.vanilla.spec.ts +++ b/packages/history-utility/src/__tests__/history-utility.vanilla.spec.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; import { HistoryNode, proxyWithHistory } from '../history-utility'; @@ -25,6 +25,30 @@ describe('proxyWithHistory: vanilla', () => { expect(state.value.count).toEqual(1); }); + it('should call onChange when provided', async () => { + const onChange = vi.fn(); + const state = proxyWithHistory({ count: 0 }, { onChange }); + await Promise.resolve(); + expect(state.value.count).toEqual(0); + + state.value.count += 1; + await Promise.resolve(); + expect(state.value.count).toEqual(1); + expect(onChange).toBeCalledWith({ count: 1 }, expect.any(Object)); + + state.undo(); + await Promise.resolve(); + expect(state.value.count).toEqual(0); + expect(onChange).toBeCalledWith({ count: 0 }, expect.any(Object)); + + state.redo(); + await Promise.resolve(); + expect(state.value.count).toEqual(1); + expect(onChange).toBeCalledWith({ count: 1 }, expect.any(Object)); + + expect(onChange).toHaveBeenCalledTimes(3); + }); + it('should provide basic sequential undo functionality', async () => { const state = proxyWithHistory({ count: 0 }); await Promise.resolve(); diff --git a/packages/history-utility/src/history-utility.ts b/packages/history-utility/src/history-utility.ts index a4e59e7..c09a403 100644 --- a/packages/history-utility/src/history-utility.ts +++ b/packages/history-utility/src/history-utility.ts @@ -40,11 +40,18 @@ export type History = { type SubscribeOps = Parameters[1]>[0]; -export type HistoryOptions = { +export type HistoryOptions = { /** * determines if the internal subscribe behaviour should be skipped. */ skipSubscribe?: boolean; + /** + * callback that triggers any time a change happens wihin the utility + * + * @param value - the changed value being tracked + * @param data - metadata of the change + */ + onChange?(value: T, data: { ops: SubscribeOps; historySaved: boolean }): void; }; const isObject = (value: unknown): value is object => @@ -68,9 +75,9 @@ const deepClone = (value: T): T => { return baseObject; }; -const normalizeOptions = ( - options?: HistoryOptions | boolean -): HistoryOptions => { +function normalizeOptions( + options?: HistoryOptions | boolean +): HistoryOptions { if (typeof options === 'boolean') { if (import.meta.env?.MODE !== 'production') { console.warn(`The second parameter of 'proxyWithHistory' as boolean is deprecated and support for boolean will be removed @@ -92,7 +99,7 @@ const normalizeOptions = ( ...defaultOptions, ...options, }; -}; +} /** * This creates a new proxy with history support (ProxyHistoryObject). @@ -128,7 +135,7 @@ const normalizeOptions = ( */ export function proxyWithHistory( initialValue: V, - options?: HistoryOptions | boolean + options?: HistoryOptions | boolean ) { const utilOptions = normalizeOptions(options); const proxyObject = proxy({ @@ -247,7 +254,14 @@ export function proxyWithHistory( */ subscribe: () => subscribe(proxyObject, (ops) => { - if (proxyObject.shouldSaveHistory(ops)) proxyObject.saveHistory(); + const shouldSaveHistory = proxyObject.shouldSaveHistory(ops); + + if (shouldSaveHistory) proxyObject.saveHistory(); + + utilOptions.onChange?.(proxyObject.value, { + ops, + historySaved: shouldSaveHistory, + }); }), // history rewrite utilities