import { ComponentMirror, Injector, InputSignal, OutputEmitterRef, reflectComponentType, Type, } from '@angular/core' type Inputs = { [K in keyof T as T[K] extends InputSignal ? K : never]?: T[K] extends InputSignal ? R : never } type Outputs = { [K in keyof T as T[K] extends OutputEmitterRef ? K : never]?: T[K] extends OutputEmitterRef ? OutputEmitterRef['emit'] : never } type OptionalKeys = K extends keyof T ? T[K] extends Required[K] ? undefined extends T[K] ? K : never : K : never interface FlexRenderRequiredOptions< TInputs extends Record, TOutputs extends Record, > { /** * Component instance inputs. They will be set via [componentRef.setInput API](https://angular.dev/api/core/ComponentRef#setInput) */ inputs: TInputs /** * Component instance outputs. */ outputs?: TOutputs /** * Optional {@link Injector} that will be used when rendering the component */ injector?: Injector } interface FlexRenderOptions< TInputs extends Record, TOutputs extends Record, > { /** * Component instance inputs. They will be set via [componentRef.setInput API](https://angular.dev/api/core/ComponentRef#setInput) */ inputs?: TInputs /** * Component instance outputs. */ outputs?: TOutputs /** * Optional {@link Injector} that will be used when rendering the component */ injector?: Injector } /** * Helper function to create a [@link FlexRenderComponent] instance, with better type-safety. * * - options object must be passed when the given component instance contains at least one required signal input. * - options/inputs is typed with the given component inputs * - options/outputs is typed with the given component outputs */ export function flexRenderComponent< TComponent = any, TInputs extends Inputs = Inputs, TOutputs extends Outputs = Outputs, >( component: Type, ...options: OptionalKeys extends never ? [FlexRenderOptions?] : [FlexRenderRequiredOptions] ) { const { inputs, injector, outputs } = options?.[0] ?? {} return new FlexRenderComponent(component, inputs, injector, outputs) } /** * Wrapper class for a component that will be used as content for {@link FlexRenderDirective} * * Prefer {@link flexRenderComponent} helper for better type-safety */ export class FlexRenderComponent { readonly mirror: ComponentMirror readonly allowedInputNames: string[] = [] readonly allowedOutputNames: string[] = [] constructor( readonly component: Type, readonly inputs?: Inputs, readonly injector?: Injector, readonly outputs?: Outputs, ) { const mirror = reflectComponentType(component) if (!mirror) { throw new Error( `[@tanstack-table/angular] The provided symbol is not a component`, ) } this.mirror = mirror for (const input of this.mirror.inputs) { this.allowedInputNames.push(input.propName) } for (const output of this.mirror.outputs) { this.allowedOutputNames.push(output.propName) } } }