From 30c9b3aa3de8122c171acd2b5e47d81293b22d98 Mon Sep 17 00:00:00 2001 From: Prev Wong Date: Mon, 29 Apr 2024 18:45:18 +0800 Subject: [PATCH] feat(reka): implement getComponentSlots() --- .changeset/wise-points-teach.md | 5 +++ packages/core/src/reka.ts | 4 ++ packages/core/src/resolver.ts | 79 ++++++++++++++++++++++++++++++++- packages/core/src/scope.ts | 15 +++++++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 .changeset/wise-points-teach.md diff --git a/.changeset/wise-points-teach.md b/.changeset/wise-points-teach.md new file mode 100644 index 00000000..c56599e8 --- /dev/null +++ b/.changeset/wise-points-teach.md @@ -0,0 +1,5 @@ +--- +'@rekajs/core': patch +--- + +Implement getComponentSlots() to quickly get slots defined in a Reka Component diff --git a/packages/core/src/reka.ts b/packages/core/src/reka.ts index 897e013b..209d8e8a 100644 --- a/packages/core/src/reka.ts +++ b/packages/core/src/reka.ts @@ -433,6 +433,10 @@ export class Reka { ); } + getComponentSlots(component: t.Component) { + return this.head.resolver.componentToSlotsMap.get(component) ?? []; + } + /** * Create a new Reka instance */ diff --git a/packages/core/src/resolver.ts b/packages/core/src/resolver.ts index 0e256d93..201739ec 100644 --- a/packages/core/src/resolver.ts +++ b/packages/core/src/resolver.ts @@ -1,4 +1,5 @@ import * as t from '@rekajs/types'; +import { invariant } from '@rekajs/utils'; import { computed, IComputedValue, @@ -32,6 +33,8 @@ export class Resolver { identifiersToIdentifiable: Map; nodeToScope: Map; + componentToSlotsMap: Map>>; + private scope: Scope; private cachedComponentResolver: WeakMap< t.Component, @@ -61,9 +64,12 @@ export class Resolver { this.nodeToScope = new Map(); + this.componentToSlotsMap = new Map(); + makeObservable(this, { identifiersToIdentifiableDistance: observable, nodeToScope: observable, + componentToSlotsMap: observable, }); } @@ -116,6 +122,58 @@ export class Resolver { }); } + private bindSlotToComponent(component: t.Component, slot: t.SlotTemplate) { + runInAction(() => { + const componentMap = this.componentToSlotsMap.get(component); + + //TODO: support named slots + const slotName = 'children'; + + if (!componentMap) { + this.componentToSlotsMap.set( + component, + new Map([[slotName, new Set([slot])]]) + ); + + return; + } + + const slotSet = componentMap.get(slotName); + + if (!slotSet) { + componentMap.set(slotName, new Set([slot])); + return; + } + + slotSet.add(slot); + }); + } + + private unbindSlotToComponent(slot: t.SlotTemplate) { + const scope = this.nodeToScope.get(slot); + const slotName = 'children'; + + if (!scope) { + return; + } + + const componentScope = scope.context?.component; + if (!componentScope) { + return; + } + + const component = this.reka.getNodeFromId( + componentScope.id, + t.RekaComponent + ); + + this.componentToSlotsMap.get(component)?.get(slotName)?.delete(slot); + + if (this.componentToSlotsMap.get(component)?.get(slotName)?.size === 0) { + this.componentToSlotsMap.get(component)?.delete(slotName); + } + } + unbindIdentifierToIdentifiable(identifier: t.Identifier) { runInAction(() => { this.identifiersToIdentifiable.delete(identifier); @@ -254,7 +312,7 @@ export class Resolver { }); if (component.template) { - this.resolveTemplate(component.template, componentScope); + this.resolveTemplate(component.template, componentScope); } }), key: scope.toString(), @@ -276,6 +334,15 @@ export class Resolver { this.bindNodeToScope(template, templateScope); + if (t.is(template, t.SlotTemplate)) { + const componentId = scope.context?.component.id; + invariant(componentId); + + const component = this.reka.getNodeFromId(componentId, t.RekaComponent); + + this.bindSlotToComponent(component, template); + } + let eachIndex: string | null = null; let eachAliasName: string | null = null; @@ -403,6 +470,16 @@ export class Resolver { return; } + if (t.is(node, t.Component)) { + runInAction(() => { + this.componentToSlotsMap.delete(node); + }); + } + + if (t.is(node, t.SlotTemplate)) { + this.unbindSlotToComponent(node); + } + const scopeId = getKeyFromScopeDescription(scopeDescription); this.scopeRegistry.delete(scopeId); diff --git a/packages/core/src/scope.ts b/packages/core/src/scope.ts index d2675ceb..66785a96 100644 --- a/packages/core/src/scope.ts +++ b/packages/core/src/scope.ts @@ -98,6 +98,10 @@ export const getScopeDescriptionByNodeOwner = ( return description; }; +export type ScopeContext = { + component?: t.Component; +}; + export class Scope { identifiables: Map; @@ -105,6 +109,8 @@ export class Scope { path: string; + context: Record | null; + constructor( readonly resolver: Resolver, descriptionOrNode: ScopeDescription | t.ASTNode, @@ -124,6 +130,15 @@ export class Scope { this.resolver.scopeRegistry.set(this.key, this); + this.context = null; + + if (parent) { + this.context = { + ...(parent.context ?? {}), + [parent.description.level]: parent.description, + }; + } + makeObservable(this, { identifiables: observable, defineIdentifiable: action,