diff --git a/libs/core/src/lib/components/wrapper-component.ts b/libs/core/src/lib/components/wrapper-component.ts index 77accbf0..a85d4348 100644 --- a/libs/core/src/lib/components/wrapper-component.ts +++ b/libs/core/src/lib/components/wrapper-component.ts @@ -24,7 +24,7 @@ import { Many } from '../declarations/many'; import { ReactContentProps } from '../renderer/react-content'; import { isReactNode } from '../renderer/react-node'; import { isReactRendererData } from '../renderer/renderer'; -import { createComponentRenderer, createHtmlRenderer, createTemplateRenderer } from '../renderer/renderprop-helpers'; +import { createComponentRenderer, createHtmlRenderer, createTemplateRenderer, RenderPropContext } from '../renderer/renderprop-helpers'; import { toObject } from '../utils/object/to-object'; import { afterRenderFinished } from '../utils/render/render-delay'; import { unreachable } from '../utils/types/unreachable'; @@ -40,11 +40,20 @@ export interface RenderComponentOptions { readonly injector: Injector; } +/** + * This interface allow to intercept and modify the default props used by defaultRender + */ +export interface RenderPropOptions { + readonly getProps: (defaultProps?: TContext) => TContext; +} + export type InputRendererOptions = | TemplateRef | ((context: TContext) => HTMLElement) | ComponentRef - | RenderComponentOptions; + | RenderComponentOptions + | RenderPropContext + | RenderPropOptions; export type JsxRenderFunc = (context: TContext) => JSX.Element; @@ -214,14 +223,18 @@ export abstract class ReactWrapperComponent implements AfterV } if (typeof input === 'object') { - const { componentType, factoryResolver, injector } = input; - const componentFactory = factoryResolver.resolveComponentFactory(componentType); - const componentRef = componentFactory.create(injector); - - // Call the function again with the created ComponentRef - return this.createInputJsxRenderer(componentRef, additionalProps); + if (input.hasOwnProperty('componentType')) { + const { componentType, factoryResolver, injector } = input; + const componentFactory = factoryResolver.resolveComponentFactory(componentType); + const componentRef = componentFactory.create(injector); + + // Call the function again with the created ComponentRef + return this.createInputJsxRenderer(componentRef, additionalProps); + } else { + // it's needed to avoid typescript error on unreachable(input) + return undefined; + } } - unreachable(input); } @@ -238,10 +251,19 @@ export abstract class ReactWrapperComponent implements AfterV additionalProps?: ReactContentProps; } ): (props?: TProps, defaultRender?: JsxRenderFunc) => JSX.Element | null { - const renderer = - (options && options.jsxRenderer) || - this.createInputJsxRenderer(renderInputValue, options && options.additionalProps); + if (typeof renderInputValue === 'object') { + if (renderInputValue.hasOwnProperty('render')) { + return (renderInputValue).render; + } + + if (renderInputValue.hasOwnProperty('getProps')) { + return (props?: TProps, defaultRender?: JsxRenderFunc) => { + return typeof defaultRender === 'function' ? defaultRender((renderInputValue).getProps(props)) : null; + }; + } + } + const renderer = (options && options.jsxRenderer) || this.createInputJsxRenderer(renderInputValue, options && options.additionalProps); return (props?: TProps, defaultRender?: JsxRenderFunc) => { if (!renderInputValue) { return typeof defaultRender === 'function' ? defaultRender(props) : null; @@ -265,7 +287,7 @@ export abstract class ReactWrapperComponent implements AfterV throw new Error( `[${(this.elementRef .nativeElement as HTMLElement).tagName.toLowerCase()}] React wrapper components cannot have the '${ - attr.name + attr.name }' attribute set. Use the following alternative: ${alternativeAttrName || ''}` ); } @@ -284,11 +306,11 @@ export abstract class ReactWrapperComponent implements AfterV const eventHandlersProps = eventListeners && Object.keys(eventListeners).length ? toObject( - Object.values(eventListeners).map<[string, React.EventHandler]>(([eventListener]) => [ - eventListener.type, - (ev: React.SyntheticEvent) => eventListener.listener(ev && ev.nativeEvent), - ]) - ) + Object.values(eventListeners).map<[string, React.EventHandler]>(([eventListener]) => [ + eventListener.type, + (ev: React.SyntheticEvent) => eventListener.listener(ev && ev.nativeEvent), + ]) + ) : {}; { } diff --git a/libs/fabric/src/lib/components/text-field/base-text-field.component.ts b/libs/fabric/src/lib/components/text-field/base-text-field.component.ts index ea11852b..d59b9349 100644 --- a/libs/fabric/src/lib/components/text-field/base-text-field.component.ts +++ b/libs/fabric/src/lib/components/text-field/base-text-field.component.ts @@ -68,6 +68,7 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent(); @Output() readonly onBeforeChange = new EventEmitter<{ newValue: any }>(); @Output() readonly onNotifyValidationResult = new EventEmitter<{ errorMessage: string; value: string | undefined }>(); + @Output() readonly onBlur = new EventEmitter<{ event: Event }>(); /* Non-React props, more native support for Angular */ // support for two-way data binding for `@Input() checked`. @@ -84,6 +85,7 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent, newValue?: string) { this.onChange.emit({ event: event.nativeEvent, newValue }); - this.valueChange.emit(newValue); } @@ -106,4 +107,8 @@ export class FabBaseTextFieldComponent extends ReactWrapperComponent) { + this.onBlur.emit({ event: event.nativeEvent }); + } } diff --git a/libs/fabric/src/lib/components/text-field/masked-text-field.component.ts b/libs/fabric/src/lib/components/text-field/masked-text-field.component.ts index 57127ac6..4afe13d0 100644 --- a/libs/fabric/src/lib/components/text-field/masked-text-field.component.ts +++ b/libs/fabric/src/lib/components/text-field/masked-text-field.component.ts @@ -58,6 +58,7 @@ import { FabBaseTextFieldComponent } from './base-text-field.component'; [Change]="onChangeHandler" [BeforeChange]="onBeforeChangeHandler" [NotifyValidationResult]="onNotifyValidationResultHandler" + [Blur]="onBlurHandler" > `, diff --git a/libs/fabric/src/lib/components/text-field/text-field.component.ts b/libs/fabric/src/lib/components/text-field/text-field.component.ts index 7aa244af..b59ebdd8 100644 --- a/libs/fabric/src/lib/components/text-field/text-field.component.ts +++ b/libs/fabric/src/lib/components/text-field/text-field.component.ts @@ -58,6 +58,7 @@ import { FabBaseTextFieldComponent } from './base-text-field.component'; [Change]="onChangeHandler" [BeforeChange]="onBeforeChangeHandler" [NotifyValidationResult]="onNotifyValidationResultHandler" + [Blur]="onBlurHandler" > `,