Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion projects/limble-tree/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class BranchComponent<T>

@Input() contentToHost?: Type<T>;

@Output() readonly contentCreated = new EventEmitter<T>();
@Output() readonly contentCreated = new EventEmitter<ComponentRef<T>>();
@Output() readonly showDropzones = new EventEmitter<"upper" | "lower">();
@Output() readonly dropped = new EventEmitter<"inner" | "lateral">();

Expand All @@ -57,7 +57,7 @@ export class BranchComponent<T>
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { ComponentRef, EventEmitter, Type } from "@angular/core";

export interface HostComponent<UserlandComponent> {
contentCreated: EventEmitter<UserlandComponent>;
contentCreated: EventEmitter<ComponentRef<UserlandComponent>>;
contentToHost?: Type<UserlandComponent>;
getHostedContent: () => ComponentRef<UserlandComponent> | undefined;
}
6 changes: 4 additions & 2 deletions projects/limble-tree/src/lib/core/branch-options.interface.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { EventEmitter, Type } from "@angular/core";
import type { EventEmitter, InputSignal, Type } from "@angular/core";

export interface BranchOptions<Component> {
inputBindings?: {
[K in keyof Component]?: Component[K];
[K in keyof Component]?: Component[K] extends InputSignal<infer U>
? U
: Component[K];
};
outputBindings?: {
[K in keyof Component]?: Component[K] extends EventEmitter<infer T>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,21 +80,25 @@ export class BranchController<UserlandComponent>
private getContentCreatedSub(
instance: BranchComponent<UserlandComponent>
): 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});

Expand Down Expand Up @@ -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<EmptyComponent>(getViewContainer());
const branch = root.grow(EmptyComponent, {
inputBindings: { testInput2: "testing" }
});
expect(branch.getUserlandComponentRef()?.instance.testInput2()).toBe(
"testing"
);
});
});
6 changes: 4 additions & 2 deletions projects/limble-tree/src/lib/legacy/legacy-tree.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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<infer U> ? U : T[K];
};
const nodeData = node;
const branch = parent.grow(component as Type<T>, {
inputBindings: bindings,
Expand Down
4 changes: 3 additions & 1 deletion projects/limble-tree/src/lib/test-util/empty.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Input } from "@angular/core";
import { Component, input, Input } from "@angular/core";

@Component({
standalone: true,
Expand All @@ -7,4 +7,6 @@ import { Component, Input } from "@angular/core";
})
export class EmptyComponent {
@Input() testInput?: string;
testInput2 = input<string>();
treeBranch = input.required();
}
Loading