-
-
Notifications
You must be signed in to change notification settings - Fork 3k
/
proxy.ts
97 lines (88 loc) 路 2.69 KB
/
proxy.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import { computed, type Signal, untracked } from '@angular/core'
import { type Table } from '@tanstack/table-core'
type TableSignal<T> = Table<T> & Signal<Table<T>>
export function proxifyTable<T>(
tableSignal: Signal<Table<T>>
): Table<T> & Signal<Table<T>> {
const internalState = tableSignal as TableSignal<T>
return new Proxy(internalState, {
apply() {
return tableSignal()
},
get(target, property: keyof Table<T>): any {
if (target[property]) {
return target[property]
}
const table = untracked(tableSignal)
/**
* Attempt to convert all accessors into computed ones,
* excluding handlers as they do not retain any reactive value
*/
if (
property.startsWith('get') &&
!property.endsWith('Handler') &&
!property.endsWith('Model')
) {
const maybeFn = table[property] as Function | never
if (typeof maybeFn === 'function') {
Object.defineProperty(target, property, {
value: toComputed(tableSignal, maybeFn),
configurable: true,
enumerable: true,
})
return target[property]
}
}
// @ts-expect-error
return (target[property] = table[property])
},
has(_, prop: keyof Table<T>) {
return !!untracked(tableSignal)[prop]
},
ownKeys() {
return Reflect.ownKeys(untracked(tableSignal))
},
getOwnPropertyDescriptor() {
return {
enumerable: true,
configurable: true,
}
},
})
}
/**
* Here we'll handle all type of accessors:
* - 0 argument -> e.g. table.getCanNextPage())
* - 0~1 arguments -> e.g. table.getIsSomeRowsPinned(position?)
* - 1 required argument -> e.g. table.getColumn(columnId)
* - 1+ argument -> e.g. table.getRow(id, searchAll?)
*
* Since we are not able to detect automatically the accessors parameters,
* we'll wrap all accessors into a cached function wrapping a computed
* that return it's value based on the given parameters
*/
function toComputed<T>(signal: Signal<Table<T>>, fn: Function) {
const hasArgs = fn.length > 0
if (!hasArgs) {
return computed(() => {
void signal()
return fn()
})
}
const computedCache: Record<string, Signal<unknown>> = {}
return (...argsArray: any[]) => {
const serializedArgs = serializeArgs(...argsArray)
if (computedCache.hasOwnProperty(serializedArgs)) {
return computedCache[serializedArgs]?.()
}
const computedSignal = computed(() => {
void signal()
return fn(...argsArray)
})
computedCache[serializedArgs] = computedSignal
return computedSignal()
}
}
function serializeArgs(...args: any[]) {
return JSON.stringify(args)
}