feat(vue): expand @robonen/vue composable collection
Composables, tests, category barrels, and README for @robonen/vue.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { isRef, reactive, unref } from 'vue';
|
||||
import type { MaybeRef, UnwrapNestedRefs } from 'vue';
|
||||
|
||||
// Shared, frozen descriptor returned by the proxy's `getOwnPropertyDescriptor`
|
||||
// trap. Every enumerable own key of the underlying ref resolves to the same
|
||||
// shape, so we allocate it once at module scope instead of per-lookup.
|
||||
const OWN_PROPERTY_DESCRIPTOR: PropertyDescriptor = {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* @name toReactive
|
||||
* @category Reactivity
|
||||
* @description Convert a ref of object to a reactive proxy. Property reads and
|
||||
* writes pass straight through to the ref's current value, so the proxy stays
|
||||
* in sync even if the ref is reassigned to a whole new object. Writing a plain
|
||||
* value onto a key that currently holds a ref unwraps into that ref's `.value`.
|
||||
* Passing a plain object simply returns `reactive(object)`.
|
||||
*
|
||||
* @param {MaybeRef<T>} objectRef A ref of object (or a plain object)
|
||||
* @returns {UnwrapNestedRefs<T>} A reactive proxy backed by the ref
|
||||
*
|
||||
* @example
|
||||
* const state = ref({ count: 0 });
|
||||
* const reactiveState = toReactive(state);
|
||||
* reactiveState.count++; // state.value.count === 1
|
||||
*
|
||||
* @example
|
||||
* // survives ref reassignment
|
||||
* const obj = ref({ name: 'a' });
|
||||
* const r = toReactive(obj);
|
||||
* obj.value = { name: 'b' };
|
||||
* r.name; // 'b'
|
||||
*
|
||||
* @since 0.0.15
|
||||
*/
|
||||
export function toReactive<T extends object>(
|
||||
objectRef: MaybeRef<T>,
|
||||
): UnwrapNestedRefs<T> {
|
||||
if (!isRef(objectRef))
|
||||
return reactive(objectRef);
|
||||
|
||||
const proxy = new Proxy({} as T, {
|
||||
get(_, key, receiver) {
|
||||
return unref(Reflect.get(objectRef.value, key, receiver));
|
||||
},
|
||||
set(_, key, value) {
|
||||
const current = objectRef.value[key as keyof T];
|
||||
|
||||
if (isRef(current) && !isRef(value))
|
||||
current.value = value;
|
||||
else
|
||||
(objectRef.value as Record<PropertyKey, unknown>)[key] = value;
|
||||
|
||||
return true;
|
||||
},
|
||||
deleteProperty(_, key) {
|
||||
return Reflect.deleteProperty(objectRef.value, key);
|
||||
},
|
||||
has(_, key) {
|
||||
return Reflect.has(objectRef.value, key);
|
||||
},
|
||||
ownKeys() {
|
||||
return Reflect.ownKeys(objectRef.value);
|
||||
},
|
||||
getOwnPropertyDescriptor() {
|
||||
return OWN_PROPERTY_DESCRIPTOR;
|
||||
},
|
||||
});
|
||||
|
||||
return reactive(proxy) as UnwrapNestedRefs<T>;
|
||||
}
|
||||
Reference in New Issue
Block a user