From 3ead2f402724b3c23fc9c26354fc71e73e93dac0 Mon Sep 17 00:00:00 2001 From: chenjiahan Date: Fri, 25 Sep 2020 11:05:26 +0800 Subject: [PATCH] feat: add useRelation composition --- src/composition/use-relation.ts | 114 +++++++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 11 deletions(-) diff --git a/src/composition/use-relation.ts b/src/composition/use-relation.ts index 53c47b2b9..e9cd8f15a 100644 --- a/src/composition/use-relation.ts +++ b/src/composition/use-relation.ts @@ -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(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 {}; +}