Skip to content

Commit

Permalink
feat(syncRef): support custom transforms (#1968)
Browse files Browse the repository at this point in the history
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
chaii3 and antfu committed Jul 24, 2022
1 parent fcaf950 commit 348d321
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 12 deletions.
24 changes: 23 additions & 1 deletion packages/shared/syncRef/index.md
Expand Up @@ -28,7 +28,7 @@ a.value = 'bar'
console.log(b.value) // bar
```

One directional
### One directional

```ts
import { syncRef } from '@vueuse/core'
Expand All @@ -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
```
49 changes: 49 additions & 0 deletions packages/shared/syncRef/index.test.ts
Expand Up @@ -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)
})
})
35 changes: 24 additions & 11 deletions 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<L, R = L> extends ConfigurableFlushSync {
/**
* Watch deeply
*
Expand All @@ -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
}
}

/**
Expand All @@ -30,34 +38,39 @@ export interface SyncRefOptions extends ConfigurableFlushSync {
* @param left
* @param right
*/
export function syncRef<R extends Ref<any>>(left: R, right: R, options: SyncRefOptions = {}) {
export function syncRef<L, R = L>(left: Ref<L>, right: Ref<R>, options: SyncRefOptions<L, R> = {}) {
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?.()
}
}

0 comments on commit 348d321

Please sign in to comment.