[Core] Allow passing additional props to ReactContent when creating render inputs
This commit is contained in:
Родитель
48b94bc594
Коммит
33f9e6c2d6
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "../../node_modules/ng-packagr/package.schema.json",
|
||||
"name": "@angular-react/core",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.4",
|
||||
"ngPackage": {
|
||||
"lib": {
|
||||
"languageLevel": [
|
||||
|
|
|
@ -16,6 +16,7 @@ import toStyle from 'css-to-style';
|
|||
|
||||
import { isReactNode } from '../renderer/react-node';
|
||||
import { renderComponent, renderFunc, renderTemplate } from '../renderer/renderprop-helpers';
|
||||
import { ExternalReactContentProps } from '../renderer/react-content';
|
||||
import { unreachable } from '../utils/types/unreachable';
|
||||
|
||||
type AttributeNameAlternative = [string, string | undefined];
|
||||
|
@ -133,24 +134,26 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
|
|||
/**
|
||||
* Create an JSX renderer for an `@Input` property.
|
||||
* @param input The input property
|
||||
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
|
||||
*/
|
||||
protected createInputJsxRenderer<TContext extends object>(
|
||||
input: InputRendererOptions<TContext>
|
||||
input: InputRendererOptions<TContext>,
|
||||
additionalProps?: ExternalReactContentProps
|
||||
): JsxRenderFunc<TContext> | undefined {
|
||||
if (input === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (input instanceof TemplateRef) {
|
||||
return (context: TContext) => renderTemplate(input, context);
|
||||
return (context: TContext) => renderTemplate(input, context, additionalProps);
|
||||
}
|
||||
|
||||
if (input instanceof ComponentRef) {
|
||||
return (context: TContext) => renderComponent(input, context);
|
||||
return (context: TContext) => renderComponent(input, context, additionalProps);
|
||||
}
|
||||
|
||||
if (input instanceof Function) {
|
||||
return (context: TContext) => renderFunc(input, context);
|
||||
return (context: TContext) => renderFunc(input, context, additionalProps);
|
||||
}
|
||||
|
||||
if (typeof input === 'object') {
|
||||
|
@ -158,7 +161,7 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
|
|||
const componentFactory = factoryResolver.resolveComponentFactory(componentType);
|
||||
const componentRef = componentFactory.create(injector);
|
||||
|
||||
return (context: TContext) => renderComponent(componentRef, context);
|
||||
return (context: TContext) => renderComponent(componentRef, context, additionalProps);
|
||||
}
|
||||
|
||||
unreachable(input);
|
||||
|
@ -168,12 +171,14 @@ export abstract class ReactWrapperComponent<TProps extends {}> implements AfterV
|
|||
* Create an event handler for a render prop
|
||||
* @param renderInputValue the value of the render `@Input` property.
|
||||
* @param jsxRenderer an optional renderer to use.
|
||||
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
|
||||
*/
|
||||
protected createRenderPropHandler<TProps extends object>(
|
||||
renderInputValue: InputRendererOptions<TProps>,
|
||||
jsxRenderer?: JsxRenderFunc<TProps>
|
||||
jsxRenderer?: JsxRenderFunc<TProps>,
|
||||
additionalProps?: ExternalReactContentProps
|
||||
): (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => JSX.Element | null {
|
||||
const renderer = jsxRenderer || this.createInputJsxRenderer(renderInputValue);
|
||||
const renderer = jsxRenderer || this.createInputJsxRenderer(renderInputValue, additionalProps);
|
||||
|
||||
return (props?: TProps, defaultRender?: JsxRenderFunc<TProps>) => {
|
||||
if (!renderInputValue) {
|
||||
|
|
|
@ -4,9 +4,10 @@ import * as ReactDOM from 'react-dom';
|
|||
const DEBUG = false;
|
||||
export const CHILDREN_TO_APPEND_PROP = 'children-to-append'; // TODO: Change to Symbol('children-to-append') after upgrade to TS 2.7.
|
||||
|
||||
export interface ReactContentProps {
|
||||
readonly 'children-to-append': HTMLElement[]; // TODO: use CHILDREN_TO_APPEND_PROP after upgrade to TS 2.7.
|
||||
|
||||
/**
|
||||
* Props that can be passed to `ReactContent` from users.
|
||||
*/
|
||||
export interface ExternalReactContentProps {
|
||||
/**
|
||||
* Experimental rendering mode.
|
||||
* Uses a similar approach to `router-outlet`, where the child elements are added to the parent, instead of this node, and this is hidden.
|
||||
|
@ -15,6 +16,12 @@ export interface ReactContentProps {
|
|||
legacyRenderMode?: boolean;
|
||||
}
|
||||
|
||||
// NOTE: Separated into `ExternalReactContentProps` since We only want a subset of props to be exposed to external users.
|
||||
// With Omit type (TS 2.8) we can simply have `type ExternalReactContentProps = Omit<ReactContentProps, 'children-to-append'>`
|
||||
export interface ReactContentProps extends ExternalReactContentProps {
|
||||
readonly 'children-to-append': HTMLElement[]; // TODO: use CHILDREN_TO_APPEND_PROP after upgrade to TS 2.7.
|
||||
}
|
||||
|
||||
/**
|
||||
* Render any `HTMLElement`s (including Angular components) as a child of React components.
|
||||
* Supports two rendering modes:
|
||||
|
@ -23,11 +30,15 @@ export interface ReactContentProps {
|
|||
* (similar to how `router-outlet` behaves in Angular).
|
||||
*/
|
||||
export class ReactContent extends React.PureComponent<ReactContentProps> {
|
||||
|
||||
componentDidMount() {
|
||||
const element = ReactDOM.findDOMNode(this);
|
||||
if (this.props[CHILDREN_TO_APPEND_PROP]) {
|
||||
if (DEBUG) { console.warn('ReactContent Component > componentDidMount > childrenToAppend:', this.props[CHILDREN_TO_APPEND_PROP]); }
|
||||
if (DEBUG) {
|
||||
console.warn(
|
||||
'ReactContent Component > componentDidMount > childrenToAppend:',
|
||||
this.props[CHILDREN_TO_APPEND_PROP]
|
||||
);
|
||||
}
|
||||
|
||||
const hostElement = this.props.legacyRenderMode ? element : element.parentElement;
|
||||
this.props[CHILDREN_TO_APPEND_PROP].forEach(child => hostElement.appendChild(child));
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import { ComponentRef, TemplateRef } from "@angular/core";
|
||||
import { ComponentRef, TemplateRef } from '@angular/core';
|
||||
import * as React from 'react';
|
||||
|
||||
import { CHILDREN_TO_APPEND_PROP, ReactContent } from "../renderer/react-content";
|
||||
import { CHILDREN_TO_APPEND_PROP, ReactContent, ExternalReactContentProps } from '../renderer/react-content';
|
||||
|
||||
function renderReactContent(rootNodes: HTMLElement[]): JSX.Element {
|
||||
return React.createElement(
|
||||
ReactContent,
|
||||
{
|
||||
[CHILDREN_TO_APPEND_PROP]: rootNodes
|
||||
} as any
|
||||
);
|
||||
function renderReactContent(rootNodes: HTMLElement[], additionalProps?: ExternalReactContentProps): JSX.Element {
|
||||
return React.createElement(ReactContent, {
|
||||
...additionalProps,
|
||||
[CHILDREN_TO_APPEND_PROP]: rootNodes,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,12 +15,17 @@ function renderReactContent(rootNodes: HTMLElement[]): JSX.Element {
|
|||
*
|
||||
* @param templateRef The template to wrap
|
||||
* @param context The context to pass to the template
|
||||
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
|
||||
*/
|
||||
export function renderTemplate<TContext extends object>(templateRef: TemplateRef<TContext>, context?: TContext): JSX.Element {
|
||||
export function renderTemplate<TContext extends object>(
|
||||
templateRef: TemplateRef<TContext>,
|
||||
context?: TContext,
|
||||
additionalProps?: ExternalReactContentProps
|
||||
): JSX.Element {
|
||||
const viewRef = templateRef.createEmbeddedView(context);
|
||||
viewRef.detectChanges();
|
||||
|
||||
return renderReactContent(viewRef.rootNodes);
|
||||
return renderReactContent(viewRef.rootNodes, additionalProps);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,11 +33,16 @@ export function renderTemplate<TContext extends object>(templateRef: TemplateRef
|
|||
*
|
||||
* @param htmlRenderFunc The function to wrap
|
||||
* @param context The context to pass to the function
|
||||
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
|
||||
*/
|
||||
export function renderFunc<TContext extends object>(htmlRenderFunc: (context: TContext) => HTMLElement, context?: TContext): JSX.Element {
|
||||
export function renderFunc<TContext extends object>(
|
||||
htmlRenderFunc: (context: TContext) => HTMLElement,
|
||||
context?: TContext,
|
||||
additionalProps?: ExternalReactContentProps
|
||||
): JSX.Element {
|
||||
const rootHtmlElement = htmlRenderFunc(context);
|
||||
|
||||
return renderReactContent([rootHtmlElement]);
|
||||
return renderReactContent([rootHtmlElement], additionalProps);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -42,10 +50,15 @@ export function renderFunc<TContext extends object>(htmlRenderFunc: (context: TC
|
|||
*
|
||||
* @param htmlRenderFunc The component reference to wrap
|
||||
* @param context The context to pass to the component as `@Input`
|
||||
* @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
|
||||
*/
|
||||
export function renderComponent<TContext extends object>(componentRef: ComponentRef<TContext>, context?: TContext): JSX.Element {
|
||||
export function renderComponent<TContext extends object>(
|
||||
componentRef: ComponentRef<TContext>,
|
||||
context?: TContext,
|
||||
additionalProps?: ExternalReactContentProps
|
||||
): JSX.Element {
|
||||
Object.assign(componentRef.instance, context);
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
|
||||
return renderReactContent([componentRef.location.nativeElement]);
|
||||
return renderReactContent([componentRef.location.nativeElement], additionalProps);
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче