feat: add useRelation composition

This commit is contained in:
chenjiahan 2020-09-25 11:05:26 +08:00
parent f28bce200f
commit 3ead2f4027

View File

@ -1,25 +1,117 @@
import { VNode, isVNode, VNodeNormalizedChildren } from 'vue';
import {
VNode,
inject,
isVNode,
provide,
computed,
reactive,
onUnmounted,
getCurrentInstance,
VNodeNormalizedChildren,
ComponentPublicInstance as PublicInstance,
ComponentInternalInstance as InternalInstance,
} from 'vue';
export function flattenVNodes(children: VNodeNormalizedChildren) {
const result: VNode[] = [];
const traverse = (children: VNodeNormalizedChildren) => {
if (!Array.isArray(children)) {
return;
}
if (Array.isArray(children)) {
children.forEach((child) => {
if (isVNode(child)) {
result.push(child);
children.forEach((child) => {
if (isVNode(child)) {
result.push(child);
if (child.component?.subTree) {
traverse(child.component.subTree.children);
}
if (child.children) {
traverse(child.children);
if (child.children) {
traverse(child.children);
}
}
}
});
});
}
};
traverse(children);
return result;
}
// sort children instances by vnodes order
export function sortChildren(
parent: InternalInstance,
publicChildren: PublicInstance[],
internalChildren: InternalInstance[]
) {
const vnodes = flattenVNodes(parent.subTree.children);
internalChildren.sort(
(a, b) => vnodes.indexOf(a.vnode) - vnodes.indexOf(b.vnode)
);
const orderedPublicChildren = internalChildren.map((item) => item.proxy!);
publicChildren.sort((a, b) => {
const indexA = orderedPublicChildren.indexOf(a);
const indexB = orderedPublicChildren.indexOf(b);
return indexA - indexB;
});
}
export function useChildren(key: string) {
const publicChildren: PublicInstance[] = reactive([]);
const internalChildren: InternalInstance[] = reactive([]);
const parent = getCurrentInstance()!;
const linkChildren = (value: any) => {
const link = (child: InternalInstance) => {
if (child.proxy) {
internalChildren.push(child);
publicChildren.push(child.proxy);
sortChildren(parent, publicChildren, internalChildren);
}
};
const unlink = (child: InternalInstance) => {
const index = internalChildren.indexOf(child);
publicChildren.splice(index, 1);
internalChildren.splice(index, 1);
};
provide(key, {
link,
unlink,
children: publicChildren,
internalChildren,
...value,
});
};
return {
children: publicChildren,
linkChildren,
};
}
export function useParent(key: string) {
const parent = inject<any | null>(key, null);
if (parent) {
const instance = getCurrentInstance();
const index = computed(() => parent.internalChildren.indexOf(instance));
parent.link(instance);
onUnmounted(() => {
parent.unlink(instance);
});
return {
parent,
index,
};
}
return {};
}