diff --git a/projects/limble-tree/package.json b/projects/limble-tree/package.json index 75c35bc..1ad4ad4 100644 --- a/projects/limble-tree/package.json +++ b/projects/limble-tree/package.json @@ -1,6 +1,6 @@ { "name": "@limble/limble-tree", - "version": "6.0.0", + "version": "6.0.1", "peerDependencies": { "@angular/common": "^19.0.0", "@angular/core": "^19.0.0", diff --git a/projects/limble-tree/src/lib/components/branch/branch.component.ts b/projects/limble-tree/src/lib/components/branch/branch.component.ts index 02e1f9c..70c70ae 100644 --- a/projects/limble-tree/src/lib/components/branch/branch.component.ts +++ b/projects/limble-tree/src/lib/components/branch/branch.component.ts @@ -40,7 +40,7 @@ export class BranchComponent @Input() contentToHost?: Type; - @Output() readonly contentCreated = new EventEmitter(); + @Output() readonly contentCreated = new EventEmitter>(); @Output() readonly showDropzones = new EventEmitter<"upper" | "lower">(); @Output() readonly dropped = new EventEmitter<"inner" | "lateral">(); @@ -57,7 +57,7 @@ export class BranchComponent this.hostedContent = this.contentContainer.createComponent( this.contentToHost ); - this.contentCreated.emit(this.hostedContent.instance); + this.contentCreated.emit(this.hostedContent); assert(this.dropzones !== undefined); const inner = this.dropzones.get(0); const lateral = this.dropzones.get(1); diff --git a/projects/limble-tree/src/lib/components/host-component.interface.ts b/projects/limble-tree/src/lib/components/host-component.interface.ts index 67f54f5..2a9b564 100644 --- a/projects/limble-tree/src/lib/components/host-component.interface.ts +++ b/projects/limble-tree/src/lib/components/host-component.interface.ts @@ -1,7 +1,7 @@ import type { ComponentRef, EventEmitter, Type } from "@angular/core"; export interface HostComponent { - contentCreated: EventEmitter; + contentCreated: EventEmitter>; contentToHost?: Type; getHostedContent: () => ComponentRef | undefined; } diff --git a/projects/limble-tree/src/lib/core/branch-options.interface.ts b/projects/limble-tree/src/lib/core/branch-options.interface.ts index 1053d61..cc666cf 100644 --- a/projects/limble-tree/src/lib/core/branch-options.interface.ts +++ b/projects/limble-tree/src/lib/core/branch-options.interface.ts @@ -1,8 +1,10 @@ -import type { EventEmitter, Type } from "@angular/core"; +import type { EventEmitter, InputSignal, Type } from "@angular/core"; export interface BranchOptions { inputBindings?: { - [K in keyof Component]?: Component[K]; + [K in keyof Component]?: Component[K] extends InputSignal + ? U + : Component[K]; }; outputBindings?: { [K in keyof Component]?: Component[K] extends EventEmitter diff --git a/projects/limble-tree/src/lib/core/tree-branch/branch-controller.ts b/projects/limble-tree/src/lib/core/tree-branch/branch-controller.ts index 846bd09..ba74a94 100644 --- a/projects/limble-tree/src/lib/core/tree-branch/branch-controller.ts +++ b/projects/limble-tree/src/lib/core/tree-branch/branch-controller.ts @@ -80,21 +80,25 @@ export class BranchController private getContentCreatedSub( instance: BranchComponent ): Subscription { - return instance.contentCreated.subscribe((userlandComponentInstance) => { - const component = userlandComponentInstance as any; + return instance.contentCreated.subscribe((userlandComponentRef) => { Object.entries( this.treeBranch.branchOptions.inputBindings ?? {} ).forEach(([key, value]) => { - component[key] = value; + userlandComponentRef.setInput(key, value); }); + const userlandComponentInstance = userlandComponentRef.instance as any; Object.entries( this.treeBranch.branchOptions.outputBindings ?? {} ).forEach(([key, value]) => { this.outputBindingSubscriptions.push( - component[key].subscribe(value) + userlandComponentInstance[key].subscribe(value) ); }); - component.treeBranch = this.treeBranch; + if ("treeBranch" in userlandComponentInstance) { + userlandComponentRef.setInput("treeBranch", this.treeBranch); + } else { + userlandComponentInstance.treeBranch = this.treeBranch; + } const dropzones = instance.dropzones; assert(dropzones !== undefined); dropzoneRenderer.registerDropzones(dropzones, this.treeBranch); diff --git a/projects/limble-tree/src/lib/core/tree-branch/tree-branch.spec.ts b/projects/limble-tree/src/lib/core/tree-branch/tree-branch.spec.ts index 5585a70..523569c 100644 --- a/projects/limble-tree/src/lib/core/tree-branch/tree-branch.spec.ts +++ b/projects/limble-tree/src/lib/core/tree-branch/tree-branch.spec.ts @@ -609,7 +609,7 @@ describe("TreeBranch", () => { it("should pass itself to the userland component as an input", () => { const branch = getStandardBranch(); expect( - (branch.getUserlandComponentRef()?.instance as any).treeBranch + (branch.getUserlandComponentRef()?.instance as any).treeBranch() ).toBe(branch); }); @@ -725,4 +725,14 @@ describe("TreeBranch", () => { const branch = root.grow(BranchGrowthComponent); expect(branch.branches().length).toBe(2); }); + + it("should be able to host a userland component which has a signal input, when a corresponding inputBinding is provided", () => { + const root = new TreeRoot(getViewContainer()); + const branch = root.grow(EmptyComponent, { + inputBindings: { testInput2: "testing" } + }); + expect(branch.getUserlandComponentRef()?.instance.testInput2()).toBe( + "testing" + ); + }); }); diff --git a/projects/limble-tree/src/lib/legacy/legacy-tree.ts b/projects/limble-tree/src/lib/legacy/legacy-tree.ts index 4d9135e..0050b83 100644 --- a/projects/limble-tree/src/lib/legacy/legacy-tree.ts +++ b/projects/limble-tree/src/lib/legacy/legacy-tree.ts @@ -1,4 +1,4 @@ -import type { Type, ViewContainerRef } from "@angular/core"; +import type { InputSignal, Type, ViewContainerRef } from "@angular/core"; import { TreeRoot, type TreeBranch, type TreeOptions, config } from "../core"; import type { LimbleTreeOptions as LegacyLimbleTreeOptions } from "./legacy-tree-options.interface"; import type { @@ -78,7 +78,9 @@ export class LegacyTree { } const bindings = (node.component?.bindings ?? options.defaultComponent?.bindings ?? - {}) as { [K in keyof T]?: T[K] | undefined }; + {}) as { + [K in keyof T]?: T[K] extends InputSignal ? U : T[K]; + }; const nodeData = node; const branch = parent.grow(component as Type, { inputBindings: bindings, diff --git a/projects/limble-tree/src/lib/test-util/empty.component.ts b/projects/limble-tree/src/lib/test-util/empty.component.ts index 2183ecb..b30103c 100644 --- a/projects/limble-tree/src/lib/test-util/empty.component.ts +++ b/projects/limble-tree/src/lib/test-util/empty.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from "@angular/core"; +import { Component, input, Input } from "@angular/core"; @Component({ standalone: true, @@ -7,4 +7,6 @@ import { Component, Input } from "@angular/core"; }) export class EmptyComponent { @Input() testInput?: string; + testInput2 = input(); + treeBranch = input.required(); }