diff --git a/packages/shared/syncRef/index.md b/packages/shared/syncRef/index.md index 34429a4d79b..9b67f6ad158 100644 --- a/packages/shared/syncRef/index.md +++ b/packages/shared/syncRef/index.md @@ -28,7 +28,7 @@ a.value = 'bar' console.log(b.value) // bar ``` -One directional +### One directional ```ts import { syncRef } from '@vueuse/core' @@ -38,3 +38,25 @@ const b = ref('b') const stop = syncRef(a, b, { direction: 'rtl' }) ``` + +### Custom Transform + +```ts +import { syncRef } from '@vueuse/core' + +const a = ref(10) +const b = ref(2) + +const stop = syncRef(a, b, { + syncConvertors: { + ltr: left => left * 2, + rtl: right => right / 2 + } +}) + +console.log(b.value) // 20 + +b.value = 30 + +console.log(a.value) // 15 +``` diff --git a/packages/shared/syncRef/index.test.ts b/packages/shared/syncRef/index.test.ts index bfbe9f90122..09b5802d1af 100644 --- a/packages/shared/syncRef/index.test.ts +++ b/packages/shared/syncRef/index.test.ts @@ -47,4 +47,53 @@ describe('syncRef', () => { expect(left.value).toBe('foobar') expect(right.value).toBe('foobar') }) + + it('works with mutual convertors', () => { + const left = ref(10) + const right = ref(1) + + syncRef(left, right, { + transform: { + ltr: left => left * 2, + rtl: right => Math.round(right / 2), + }, + }) + + // check immediately sync + expect(right.value).toBe(20) + expect(left.value).toBe(10) + + left.value = 30 + expect(right.value).toBe(60) + expect(left.value).toBe(30) + + right.value = 10 + expect(right.value).toBe(10) + expect(left.value).toBe(5) + }) + + it('works with only rtl convertor', () => { + const left = ref(10) + const right = ref(2) + + syncRef(left, right, { + direction: 'rtl', + transform: { + rtl: right => Math.round(right / 2), + }, + }) + + // check immediately sync + expect(right.value).toBe(2) + expect(left.value).toBe(1) + + left.value = 10 + expect(right.value).toBe(2) + expect(left.value).toBe(10) + + right.value = 10 + + expect(right.value).toBe(10) + expect(left.value).toBe(5) + }) }) diff --git a/packages/shared/syncRef/index.ts b/packages/shared/syncRef/index.ts index 8a187a02316..975c7126dde 100644 --- a/packages/shared/syncRef/index.ts +++ b/packages/shared/syncRef/index.ts @@ -1,8 +1,8 @@ -import type { Ref } from 'vue-demi' +import type { Ref, WatchStopHandle } from 'vue-demi' import { watch } from 'vue-demi' import type { ConfigurableFlushSync } from '../utils' -export interface SyncRefOptions extends ConfigurableFlushSync { +export interface SyncRefOptions extends ConfigurableFlushSync { /** * Watch deeply * @@ -17,11 +17,19 @@ export interface SyncRefOptions extends ConfigurableFlushSync { immediate?: boolean /** - * Direction of syncing + * Direction of syncing. Value will be redefined if you define syncConvertors * * @default 'both' */ direction?: 'ltr' | 'rtl' | 'both' + + /** + * Custom transform function + */ + transform?: { + ltr?: (left: L) => R + rtl?: (right: R) => L + } } /** @@ -30,34 +38,39 @@ export interface SyncRefOptions extends ConfigurableFlushSync { * @param left * @param right */ -export function syncRef>(left: R, right: R, options: SyncRefOptions = {}) { +export function syncRef(left: Ref, right: Ref, options: SyncRefOptions = {}) { const { flush = 'sync', deep = false, immediate = true, direction = 'both', + transform = {}, } = options - let stop1: Function, stop2: Function + let watchLeft: WatchStopHandle + let watchRight: WatchStopHandle + + const transformLTR = transform.ltr ?? (v => v) + const transformRTL = transform.rtl ?? (v => v) if (direction === 'both' || direction === 'ltr') { - stop1 = watch( + watchLeft = watch( left, - newValue => right.value = newValue, + newValue => right.value = transformLTR(newValue) as R, { flush, deep, immediate }, ) } if (direction === 'both' || direction === 'rtl') { - stop2 = watch( + watchRight = watch( right, - newValue => left.value = newValue, + newValue => left.value = transformRTL(newValue) as L, { flush, deep, immediate }, ) } return () => { - stop1?.() - stop2?.() + watchLeft?.() + watchRight?.() } }