Added docs

Ben Grynhaus 2019-03-17 23:45:54 +02:00 коммит произвёл Ben Grynhaus
Коммит 79313af4e2
24 изменённых файлов: 1157 добавлений и 0 удалений

8
.vscode/settings.json поставляемый Normal file

@ -0,0 +1,8 @@
{
"cSpell.words": [
"Angular's",
"HOCs",
"readonly",
"renderable"
]
}

@ -0,0 +1,23 @@
The component registry (`registry.ts` module) holds a mapping of component names to their respective type. e.g. `"Button"` -> `Button`.
This is needed since Angular templates on the one hand are plain strings (that get parsed), and on the other `React.createElement` relies on types to know what components to create (or strings, for native HTML elements).
## Registration
Each React component that should be renderer-able needs to be registered in the component registry by calling the `registerElement(elementName, resolver)` function.
This is usually done in Wrapper libraries' individual `NgModule`s's `constructor`:
```typescript
export class FabCheckboxModule {
constructor() {
registerElement('Checkbox', () => Checkbox);
}
}
```
The `registerElement` function can be called multiple times. Any call to an already-registered `elementName` is discarded, for simplicity's sake with Angular and lazy-loaded modules. See the docs within the code for more info on that.
## Caveats
The registry is a singleton one, much like the way `ReactRenderer` is currently, but there are no technical challenges that prevent changing this in the future. See [limitations](./Limitations.md) for more info on this.

@ -0,0 +1,25 @@
## Package separation
`@angular-react` is split into 2 main parts:
1. Core (`@angular-react/core`)
2. Wrapper libraries (`@angular-react/fabric`, `@angular-react/semantic-ui`)
The core library takes care of the heavy lifting, orchestrates the rendering of the React components insider Angular, interoperability between Angular and React and provide some functionality that's commonly needed when creating wrapper components (more on that below).
Wrapper libraries create what's called "wrapper components" around React components exposed by the native UI library, as well as handle all the plumbing around binding them to Angular, the logic in these libraries is thus usually pretty limited.
## Concepts
As you can expect, you need to know Angular and React, and in addition to the regular stuff like components' lifecycle etc. you also need a good understanding of how Angular rendering works ([summary](https://alligator.io/angular/using-renderer2/), [full API docs](https://angular.io/api/core/Renderer2)), and how React rendering works in the browser ([`ReactDOM`](https://reactjs.org/docs/react-dom.html)).
Additionally, there are a few concepts that are important to know when dealing with `@angular-react`, and more specifically - `@angular-react/core`:
- `ReactRenderer` - an implementation of Angular's `Renderer2`. This is what drives the whole library and bootstraps the rendering process for each component.
- `ReactNode` - a container of a specific React component instance, the renderer holds a `Set` of these to keep track of what to render. Note that these only exist for the "root" of the React render tree, [more on that here](https://TODO).
There are a few more helper React components that we use
- `ReactContent` - a React component used for projecting `<ng-content>` inside React components.
- `ReactTemplate` - a React component used for projecting `<ng-template>`/`TemplateRef` inside React components.
- `Disguise` - a React components used when a React component has a strict parent-child relationship (e.g. checking the type of `children` in the parent component). See [caveats](https://TODO) for more info on use-cases.

@ -0,0 +1,36 @@
The render flow of a sample app is described by the below diagram, Fabric is used to demonstrate things, but the same ideas translate over to any other wrapper library:
![render-flow](./render-flow.svg)
- Things in <span style="color:#F9F7ED">brown</span> are app modules and components.
- Things in <span style="color:#EEEEEE">gray</span> are from `office-ui-fabric-react` (or any other React UI library).
- Things in <span style="color:#D5E8D4">green</span> are from `@angular-react/core`.
- Things in <span style="color:#F8CECC">red</span> are from `@angular-react/fabric` (or any other wrapper library).
## The flow
### Initial bootstrap (in general and of each component)
Usually Angular apps' `AppModule` include [`BrowserModule`](https://angular.io/api/platform-browser/BrowserModule). When using `@angular-react`, you replace this with `AngularReactBrowserModule`, which in turn renders **all components in the app**.
When a component is created, Angular creates a renderer (`Renderer2`) for it, using the renderer factory `ɵDomRendererFactory2`. We `extend ɵDomRendererFactory2`, and when `createRenderer(element, type)` is called, we either return a singleton instance of `ReactRenderer`, or delegate the work to `super` - depending on whether the element is a React wrapper one or not, respectively.
### `RenderRenderer`
`ReactRenderer` is an implementation of Angular's `Renderer2`, which for the most part - is just a manager of `ReactNode`s, mostly for adding and removing them from the DOM. The `ReactNode`s themselves handle the React rendering part and interoperability with Angular (or delegate it further down to other stuff).
### `ReactNode`
Finally, a `ReactNode` is what introduces React into Angular-land (all other stuff up until now had nothing to do with the `react` or `react-dom` packages, and would theoretically translate over to other frameworks like Vue etc.).
A `ReactNode` is a logical representation of everything needed to render a React element in the DOM, and includes two key components:
1. `type` - the type of React element to render. Either a `React.ReactType` or a `string`.
2. `domElement` - the DOM element to render to.
Furthermore, it also includes methods to update its underlying React element, these include (but not limited to):
- `setProperties` - pass down `props` to the React element.
- `addChild` - to add another `ReactNode` as a child of this one.
- `render` - trigger a render of the node.
- Calls `ReactDOM.render()` under the hood, so think of this like React's `render`, without the VDOM abstraction. This does indeed mean calling this method is costly, as you may expect.
- `destroyNode` - trigger `ReactDOM.unmountComponentAtNode()` on the node.

10
Component-hierarchy.md Normal file

@ -0,0 +1,10 @@
The component hierarchy in a typical app is described by the below diagram, Fabric is used to demonstrate things, but the same ideas translate over to any other wrapper library:
![component-hierarchy](./component-hierarchy.svg)
- Things in <span style="color:#F8CECC">red</span> are Angular components (`@Component`).
- Surrounded by their respective `NgModule`s.
- Things in <span style="color:#DAE8FC">blue</span> are React components (in any form - either class or functional).
This is a pretty small app, but the same idea holds true for larger apps with dozens of `NgModule`s.
> Note that you can also create a `FabricModule` in your app and `import` + `export` all `Fab*Module`s there, similarly to other UI libraries. There are pros and cons to each approach, so choose what works best for your use-case.

11
Event-handling.md Normal file

@ -0,0 +1,11 @@
While there is no special handling in the `ReactRenderer` for event handlers, usually wrapper libraries will use the [`ReactWrapperComponent`](./Wrapper-libraries:-ReactWrapperComponent.md) to simplify their wrapper components authoring. In that case, in addition to any usual `@Output` that the wrapper component may have, you can specify any arbitrary event handler that React supports as an `(output)`, and get that emitted as usual. e.g.:
```html
<fab-checkbox (onFocus)="handleFocus($event)"></fab-checkbox>
```
This reduces boilerplate code that wrapper components need to write, in addition to supporting any arbitrary event that the underlying DOM element supports.
This mechanism is achieved using a fork of the the [`geteventlisteners`](https://github.com/colxi/getEventListeners) package (see `renderer/geteventlisteners.ts` for the full implementation and more details).
This monkey-patches the `Element` prototype in order to capture event listener registrations done by Angular (since there is no public API that covers that as far as we could find. Changes are welcomed in this area).

10
Future-directions.md Normal file

@ -0,0 +1,10 @@
While the library is pretty stable and has not seen major overhauls in the last while, it could always be improved. We welcome both suggestions and contributions from the community.
Here are some partial list of future directions that this library could explore:
- `@angular-vue` - Just like it sounds like. `@angular-react` was created with React in mind, but is not limited to it, and most of the concepts translate well over to other VDOM-based libraries, like [Vue](https://vuejs.org/).
As described in the [render flow](./Architecture:-Render-flow.md#ReactNode), most of the abstraction logic is not specific to React.
We can easily see someone needing the same thing as `@angular-react` for embedding Vue within Angular, and could leverage most of the code in `@angular-react/core` for Vue as well. While it would certainly be a separate library, we're open to the idea of creating a shared, library-agnostic core (on the side that is not Angular).<br>
This is not unlike the separation and sharing done with [`dom-testing-library`](https://github.com/kentcdodds/dom-testing-library) and [`react-testing-library`](https://github.com/kentcdodds/react-testing-library), which the former is the base for [`vue-testing-library`](https://github.com/dfcook/vue-testing-library) and [more](https://github.com/kentcdodds/dom-testing-library#implementations).

102
Home.md Normal file

@ -0,0 +1,102 @@
Angular-React is built of two main parts:
<!-- copied over from `Architecture:-High-level.md` -- start -->
1. Core (`@angular-react/core`)
2. Wrapper libraries (`@angular-react/fabric`, `@angular-react/semantic-ui`)
The core library takes care of the heavy lifting, orchestrates the rendering of the React components insider Angular, interoperability between Angular and React and provide some functionality that's commonly needed when creating wrapper components (more on that below).
Wrapper libraries create what's called "wrapper components" around React components exposed by the native UI library, as well as handle all the plumbing around binding them to Angular, the logic in these libraries is thus usually pretty limited.
<!-- copied over from `Architecture:-High-level.md` -- end -->
## Idealogy
<!-- copied over from `Wrapper-libraries.md` -- start -->
What separates Angular-React from [other wrapper libraries](https://www.npmjs.com/search?q=angular%20react), besides the fact that most solutions in this space are between AngularJS (aka Angular 1.x) and React is that we believe that application developers should focus on what they're already doing - writing Angular apps.
Other solutions create simple bindings between the two libraries/frameworks, on-demand, but avoid dealing with the caveats of merging two view libraries together, and require you to know both libraries well enough, and deal with concepts that may be foreign to you, as an Angular developer - like HOCs, render props etc as well as dealing with change detection.
What Angular-React aims to do is bridge the gap, such that the experience for the end developer of the app is one where they can keep thinking in "Angular" as much as possible, while still using other libraries under the hood.
Below assumes you're going to use `@angular-react/fabric`, to integrate the [`office-ui-fabric-react`](https://github.com/OfficeDev/office-ui-fabric-react) library into your Angular app.
<!-- copied over from `Wrapper-libraries.md` -- end -->
## Using `@angular-react`
Integrating `@angular-react` into your existing Angular project is pretty straightforward:
1. `npx install-peerdeps --save @angular-react/fabric`
2. In your `AppModule`, change `BrowserModule` to `AngularReactBrowserModule`.
> Just like `BrowserModule`, `AngularReactBrowserModule` should only be imported once, at the top of the module hierarchy
```typescript
import { NgModule } from '@angular/core';
import { AngularReactBrowserModule } from '@angular-react/core';
@NgModule({
imports: [
AngularReactBrowserModule,
...
],
...
})
export class AppModule {}
```
3. In each component that would like use use an `@angular-react/fabric` component's `NgModule` import the relevant Fabric module. e.g.:
```typescript
import { FabButtonModule } from '@angular-react/fabric';
@NgModule({
imports: [
FabButtonModule,
],
declarations: [CounterComponent]
})
export class MyFeatureModule {
```
4. Use the component(s) exposed by the module:
```html
<!-- counter.component.html -->
<fab-default-button [text]="count" (onClick)="count++"></fab-default-button>
```
<!-- copied over from Component-hierarchy.md -- start -->
<!-- copied over from Component-hierarchy.md -- end -->
> Note that you can also create a `FabricModule` in your app and `import` + `export` all `Fab*Module`s there, similarly to other UI libraries. There are pros and cons to each approach, so choose what works best for your use-case.
## App requirements
Currently `@angular-react/core` requires the following:
- `@angular/*@^7.0.3`
- `rxjs@^6.2.0`
- `react@^16.6.3`
- `react-dom@^16.6.3`
Additional dependencies may be required, based on your UI library of choice:
- `@angular-react/fabric`:
- `office-ui-fabric-react@6.151.0` (other versions may work as well, but it is not guaranteed)
- `@angular-react/semantic-ui`:
- `semantic-ui-react@^0.79.1`
> Always consult the `package.json` for the latest dependencies versions required.
> In case of a conflict between the above and `package.json`, prefer to use the ones in the the `package.json`.
## Browser support
Browser support is determined by the lowest common dominator of all libraries:
- Angular
- React
- Your UI library of choice (e.g. `@angular-react/fabric` -> `office-ui-fabric-react`)
- [See the browser support for `office-ui-fabric-react`](https://github.com/OfficeDev/office-ui-fabric-react/wiki/Browser-Support#browser-support)

17
Limitations.md Normal file

@ -0,0 +1,17 @@
There are a few limitations that currently exist in Angular-React:
- Multiple React components can't have the same name [more info](./Architecture:-Component-registry.md)
- Possible solutions include adding namespaces to the components, as an addition argument, similarly to the `styles: ['react-renderer']` in used today. e.g. `styles: ['react-renderer', 'fabric']` to differentiate from other libraries.
It's very unlikely that a single UI library will export multiple components under the same name, so this should be enough.
- [React Context](https://reactjs.org/docs/context.html) doesn't work for sharing data between components that are not part of the same render tree.
In practice, this means that things like the `<Fabric>` component from `office-ui-fabric-react` don't have any affect, since they rely on Context to pass the `theme` down to the individual components.
- This is one of the open issues in our [To dos](./To-do.md) - using React Portals.
## Notes
_Most (if not all) don't have any technical barrier, but rather need the investment of developing them, and most of the foundation should be in place._

21
To-do.md Normal file

@ -0,0 +1,21 @@
In addition to what's described in [future directions](./Future-directions.md), there are some things that we'd like to add to the library:
## `@angular-react/core`
- Explore the option of replacing the usage of `styles` (with `'react-renderer'`) with `data`, which seems more appropriate for this sort of thing.
- The issue with this is that the `ReactRenderer` is currently a singleton, meaning that the `data` is shared across all components, regardless of the component being rendered.
- If this is not possible, at least export `'react-renderer'` as a const and import it as needed in wrapper components, if possible (the Angular compiler may complain, at least in AOT compilation).
- Explore using [React Portals](https://reactjs.org/docs/portals.html) instead of regular `ReactDOM.render()` for every wrapper React component.
As described in [limitations](./Limitations.md#ReactPortals), currently each component instance is isolated in its own React VDOM tree. Meaning that things that aren't explicitly passed down between components are not shared (like [Context](https://reactjs.org/docs/context.html)).
There is a good claim to be made that only a root element should be rendered using `ReactDOM.render()`, and then subsequent ones using [`ReactDOM.createPortal()`](https://reactjs.org/docs/portals.html), to allow Context etc. to be accessible from all React elements in the tree. Using React Portals introduces some complexity (like [event bubbling](https://reactjs.org/docs/portals.html#event-bubbling-through-portals)), but we thing it may be worth it, as either an opt-in, or an opt-out feature, per component.
There is no technical limitation blocking this, and we are open for community feedback and contributions on this as well.
## `@angular-react/fabric`
- Add more wrapper components for components in `office-ui-fabric-react`.
- Create more declarative patterns where applicable ([#3](https://github.com/Microsoft/angular-react/issues/3)).

14
Wrapper-libraries.md Normal file

@ -0,0 +1,14 @@
While they don't contain much logic in regards to handling the DOM, rendering and internal interaction between React and Angular, they are the "glue" between a specific React UI library and Angular.
Currently we have two such wrapper libraries:
1. [`@angular-react/fabric`](./Wrapper-libraries:-Fabric.md) - wrapper library for [`office-ui-fabric-react`](https://github.com/OfficeDev/office-ui-fabric-react/)
2. [`@angular-react/semantic-ui`](./Wrapper-libraries:-Semantic-UI.md) - wrapper library for [`Semantic-UI-React`](https://github.com/Semantic-Org/Semantic-UI-React)
## Idealogy
What separates Angular-React from [other wrapper libraries](https://www.npmjs.com/search?q=angular%20react), besides the fact that most solutions in this space are between AngularJS (aka Angular 1.x) and React is that we believe that application developers should focus on what they're already doing - writing Angular apps.
Other solutions create simple bindings between the two libraries/frameworks, on-demand, but avoid dealing with the caveats of merging two view libraries together, and require you to know both libraries well enough, and deal with concepts that may be foreign to you, as an Angular developer - like HOCs, render props etc as well as dealing with change detection.
What Angular-React aims to do is bridge the gap, such that the experience for the end developer of the app is one where they can keep thinking in "Angular" as much as possible, while still using other libraries under the hood.

@ -0,0 +1,115 @@
Authoring a wrapper library is pretty straightforward, and you can reference other ones, especially `@angular-react/fabric`.
The basic steps are as follows:
1. Create a [standard Angular library](https://angular.io/guide/creating-libraries) (`ng generate library my-lib`)
2. Add `@angular-react/core` to your `peerDependencies`.
3. For each component in the underlying UI library, as a set of components (that go together), create an `NgModule` as follows (we're using `"my"` as the prefix in this example):
```typescript
import { registerElement } from '@angular-react/core';
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
import { MyCounter } from 'my-react-ui-library';
import { MyCounterComponent } from './counter.component';
const components = [MyCounterComponent];
@NgModule({
declarations: components,
exports: components,
schemas: [NO_ERRORS_SCHEMA],
})
export class MyCounterModule {
constructor() {
registerElement('MyCounter', () => MyCounter);
}
}
```
And the accompanying component:
```typescript
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
Input,
OnInit,
Renderer2,
ViewChild,
} from '@angular/core';
import {
InputRendererOptions,
JsxRenderFunc,
ReactWrapperComponent,
} from '@angular-react/core';
import { MyCounterProps } from 'my-react-ui-library';
@Component({
selector: 'my-counter',
exportAs: 'myCounter',
template: `
<Counter
#reactNode
[count]="count"
[RenderCount]="renderCount && onRenderCount"
>
</Counter>
`,
styles: ['react-renderer'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FabBreadcrumbComponent
extends ReactWrapperComponent<MyCounterProps>
implements OnInit {
@ViewChild('reactNode') protected reactNodeRef: ElementRef;
@Input() count?: MyCounterProps['count'];
@Input() renderCount?: InputRendererOptions<MyCountRenderProps>;
@Output() readonly onIncrement = new EventEmitter<{ count: number }>();
onRenderCount: (
props?: MyCountRenderProps,
defaultRender?: JsxRenderFunc<MyCountRenderProps>
) => JSX.Element;
constructor(
elementRef: ElementRef,
changeDetectorRef: ChangeDetectorRef,
renderer: Renderer2
) {
super(elementRef, changeDetectorRef, renderer, { setHostDisplay: true });
}
ngOnInit() {
this.onRenderCount = this.createRenderPropHandler(this.renderCount);
}
}
```
For further explanation see [ReactWrapperComponent](./Wrapper-libraries:-ReactWrapperComponent.md), as well as the inline-documentation in the code.
## Conventions
- Pick a prefix for your wrapper library, and use it in your modules, components (including selectors) etc.
- e.g. `@angular-react/fabric` uses "fab" as the prefix, meaning that every `NgModule` and `@Component` is prefixed with `Fab*`, and every selector is prefixed with `fab-*`
- `extend` [`ReactWrapperComponent`](./Wrapper-libraries:-ReactWrapperComponent.md) in your components, to gain the benefits of things handled in `@angular-react/core`
- See more conventions in [ReactWrapperComponent](./Wrapper-libraries:-ReactWrapperComponent.md)
## Templates
Wrapper component templates are a hybrid between Angular and React. To be more specific, they're Angular templates, with React component type names as their elements. See more details on this in [component registry](./Architecture:-Component-registry.md).
In addition, the Angular compiler prevents some stuff from being defined as `Input`s, namely - you can't have `@Input`s prefixed with `on`:
> Binding to event property 'onFoo' is disallowed for security reasons, please use (Foo)=...
> If 'onFoo' is a directive input, make sure the directive is imported by the current module.Angular
This error, and the suggested workaround are **not applicable** in our use-case, since they do need to be inputs (for example, function that return a value, or render props).
Therefore, as a convention - any `Input` which is PascalCased is prefixed with `on` when inheriting from [ReactWrapperComponent](./Wrapper-libraries:-ReactWrapperComponent.md).
In addition - there's one reserved prop name that can't be used - `key`, since it's already being used by React, and we leverage that in more ways that usual internally.
Lastly, when `extend`-ing [ReactWrapperComponent](./Wrapper-libraries:-ReactWrapperComponent.md) there are a few more restrictions, [see there for more details](./Wrapper-libraries:-ReactWrapperComponent.md#Restrictions).

@ -0,0 +1,9 @@
Wrapper library for [`office-ui-fabric-react`](https://github.com/OfficeDev/office-ui-fabric-react), exposed as `@angular-react/fabric` and actively maintained and [published on npm](https://www.npmjs.com/package/@angular-react/fabric).
Contains quite a good coverage as far as wrapping the React components go, and for the wrapped components allows passing in all props as `Input`s, listening to all events via `Output`s, as well as pass in render props where applicable (see more details in [ReactWrapperComponent](./Wrapper-libraries:-ReactWrapperComponent.md#Render-props)).
In addition, some components have alternative declarative syntax for things that would make more sense as such in Angular-world, like `<fab-command-bar>` allowing using it's items in a declarative manner, via `<fab-command-bar-item>`. These make authoring apps much simpler in a lot of cases, leveraging Angular's data-binding to it's full extend, instead of changing things imperatively. These should be added when it makes sense and makes using the wrapper components easier, though it does introduce more complexity.
## Guidelines
- When `office-ui-fabric-react` marks a prop as deprecated, we remove it the next time around we upgrade our dependency on it. We're more harsh with deprecation since the wrapper components are currently authored & maintained completely manually (see [#91](https://github.com/Microsoft/angular-react/issues/91)), and maintaining more props means more manual work.

@ -0,0 +1,50 @@
> Render any `HTMLElement`s as a child of React components.
This is mainly used to passing in `<ng-content>` as `children` to React.
### Usage
When authoring wrapper components that need to use:
- `<ng-content>`, wrap it in a `ReactContent`:
```html
<Button [text]="text">
<ReactContent>
<ng-content></ng-content>
</ReactContent>
</CompoundButton>
```
- Render props:
Use `ReactWrapperComponent`'s `createRenderPropHandler()` or `createInputJsxRenderer()` if possible. if not - see [creation](#creation)
### API
```typescript
interface ReactContentProps {
/**
* Use the legacy 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.
*
* @default false
*/
legacyRenderMode?: boolean;
}
```
`ReactContent` by default will use a similar approach to what `<router-outlet>` does in Angular - render the HTML content as a sibling, and set `display: none` on itself.
If you need to out-out of this behavior for some reason, you can pass in `legacyRenderMode` as `true` to get the old behavior, where the DOM nodes get appended **as children** of the `<react-content>` element.
This may change in the future, depending on work done in that regard. See `TODO`s in the relevant code for more info on directions and possibilities.
### Creation
Since it uses some APIs that do not translate over directly to JSX as you'd expect, use the `createReactContentElement` factory function to create it.
Alternatively, when using the [ReactWrapperComponent](./Wrapper-libraries:-ReactWrapperComponent.md) this is handled for you when creating render props.
```
```

@ -0,0 +1,18 @@
> Render an `<ng-template>` as a child of a React component.
Similarly to [`ReactContent`](./Wrapper-libraries:-Helper-components:-ReactContent.md), this component is used to allow interoperability between Angular and React, in a seamless way for the app developer. One of the ways to achieve that is by allowing to pass in Angular `<ng-template>`s as render props to React ([more on that here](./Wrapper-libraries:-ReactWrapperComponent.md#Render-props)).
## Usage
The main usage of this is to translate `TemplateRef` into a React-renderable component, this is done in a similar fashion to `ReactContent`, by rendering the `TemplateRef` ourselves (in code) and then appending the `rootNodes` it exposes to the DOM manually.
To create a `ReactTemplate` element from a `TemplateRef`, use the `createReactTemplateElement()` factory function.
### API
Similarly to `ReactContent`, `ReactTemplate` has a similar props API. See [more details](./Wrapper-libraries:-Helper-components:-ReactContent.md#API) there.
## Caveats
Angular doesn't expose much of the API of templates as public APIs, and since re-rendering them can be triggered by almost anything in their scope, it's not always possible to easily tell when to re-render them, after manually rendering them and putting them inside React.
Therefore, currently we do manual change detection on these every few hundred ms, which proved to work fine, but is not ideal - any better solutions to this problem are welcomed.

@ -0,0 +1,45 @@
An `abstract class` which provides a lot of help in regards to authoring wrapper libraries' components.
The full API documentation is in the code (`wrapper-component.ts`), but essentially, all wrapper components should `extend` it. Here are some of the more important stuff it handles for you as a wrapper component author:
1. Pass down all attributes to the underlying React components
- Note that this does not cover `Input`s, since those are not plain strings and cannot be attached to the DOM [_as-is_]
2. Allow automatically setting the `display` of the wrapper component, based on the root node of the wrapper React element (e.g. if you render an `<Expander>` which then renders a `<div>`, the `display` of your wrapper component would be set to `block` at runtime) - see `WrapperComponentOptions`
3. Allow passing in arbitrary events that React can handle automatically
4. [Much] Easier creation and handling of render props - see `createRenderPropHandler` and `createInputJsxRenderer`
1. Automatic change detection for render props containing templates (by passing in an `NgZone` in `WrapperComponentOptions`)
## Render props
Render props as a concept does not translate directly to Angular. The closest concept to it is an `<ng-template>` (the main difference between the two being that the later also has change detection via the implicit context its defined and use in - i.e. the template closure or component).
To still allow Angular apps to use the concepts they know inside React components, we created the `InputRendererOptions` type:
```typescript
export type InputRendererOptions<TContext extends object> =
| TemplateRef<TContext>
| ((context: TContext) => HTMLElement)
| ComponentRef<TContext>
| RenderComponentOptions<TContext>;
export interface RenderComponentOptions<TContext extends object> {
readonly componentType: Type<TContext>;
readonly factoryResolver: ComponentFactoryResolver;
readonly injector: Injector;
}
```
This allows passing any number of types as what will be then translated over to render props.
For more info on how these are then used see the inline code documentation, as well as the "Helper components" pages ([ReactContent](./Wrapper-libraries:-Helper-components:-ReactContent.md), [ReactTemplate](./Wrapper-libraries:-Helper-components:-ReactTemplate.md), [Disguise](./Wrapper-libraries:-Helper-components:-Disguise.md)).
## Restrictions
When `extend`-ing from this component, there are a few restrictions being added, to make the lives of everyone easier and enforcing the message that these are just wrapper components, and shouldn't have their own styling, take their own space in the DOM etc.
- Passing in the `class` attribute is forbidden. Use `contentClass` instead, which leverages [`classnames`](https://github.com/JedWatson/classnames) to allow both the familiar `class` syntax as well as one that's more like [`NgClass`](https://angular.io/api/common/NgClass)' one.
- Passing in the `style` attribute is forbidden. Use `contentStyle` instead, which allows using both the familiar `style` string-based syntax, as well as one that's more like [`NgStyle`](https://angular.io/api/common/NgStyle) (leveraging [`stylenames`](https://github.com/kmathmann/stylenames)).
## Caveats
As mentioned above, wrapper components **should not have styling of their own**. Ideally, they'd all be set with [`display: contents`](https://developer.mozilla.org/en-US/docs/Web/CSS/display#display_contents) on themselves, removing them from the document flow. However, [browser support for this is partial](https://caniuse.com/#feat=css-display-contents), and to not break Edge and IE we don't use it at the moment. See [#102](https://github.com/Microsoft/angular-react/issues/102) for more info and follow up on that.

@ -0,0 +1,7 @@
Wrapper library for [`Semantic-UI-React`](https://github.com/Semantic-Org/Semantic-UI-React), exposed as `@angular-react/semantic-ui`.
## Notes
This library is just a Proof-of-Concept, and is not being actively maintained, and is **not published** to npm.
Having another library in place, in addition to the Fabric one - ensures that we don't couple `@angular-react/core` with a specific component or implementation.

21
_Sidebar.md Normal file

@ -0,0 +1,21 @@
- [Home](./Home)
- [Component hierarchy](./Component-hierarchy)
- [Event handling](./Event-handling)
- [Limitations](./Limitations)
- [To do](./To-do)
- [Future directions](./Future-directions)
- [Wrapper libraries](./Wrapper-libraries)
- [Fabric](./Wrapper-libraries%3A-Fabric)
- [Semantic UI](./Wrapper-libraries%3A-Semantic-UI)
- [Authoring](./Wrapper-libraries%3A-Authoring)
- [ReactWrapperComponent](./Wrapper-libraries%3A-ReactWrapperComponent)
- Helper components
- [ReactContent](./Wrapper-libraries%3A-Helper-components%3A-ReactContent)
- [ReactTemplate](./Wrapper-libraries%3A-Helper-components%3A-ReactTemplate)
- [Disguise](./Wrapper-libraries%3A-Helper-components%3A-Disguise)
- Architecture
- [High level](./Architecture%3A-High-level)
- [Render flow](./Architecture%3A-Render-flow)
- [Component registry](./Architecture%3A-Component-registry)
[//]: # 'generated by https://www.npmjs.com/package/github-wiki-sidebar'

578
component-hierarchy.svg Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

После

Ширина:  |  Высота:  |  Размер: 50 KiB

32
options.json Normal file

@ -0,0 +1,32 @@
{
"menu": {
"item": "{{#levelRepeat}} {{/levelRepeat}}* [{{{title}}}]({{{link}}})\n",
"category": "{{#levelRepeat}} {{/levelRepeat}}* {{#link}}[{{{title}}}]({{{link}}}){{/link}}{{^link}}{{{title}}}{{/link}}\n{{{subitems}}}",
"category-1": "{{{subitems}}}\n"
},
"rules": {
"exclude": [],
"order": [
"Home.md",
"Component-hierarchy.md",
"Event-handling.md",
"Limitations.md",
"To Do.md",
"Future-directions.md",
"Wrapper-libraries.md",
"Wrapper-libraries:-Component-templates.md",
"Wrapper-libraries:-ReactWrapperComponent.md",
"Wrapper-libraries:-Helper-components:-ReactContent.md",
"Wrapper-libraries:-Helper-components:-ReactTemplate.md",
"Wrapper-libraries:-Helper-components:-Disguise.md",
"Architecture:-High-level.md",
"Architecture:-Render-flow.md",
"Architecture:-Component-registry.md"
]
},
"separator": ":-",
"linkTemplate": "./%s",
"menuFile": "_Sidebar.md",
"multilang": false,
"menuOnly": true
}

2
raw/README.md Normal file

@ -0,0 +1,2 @@
Load `raw.xml` in [draw.io](https://www.draw.io/) to edit, then re-save here to `raw.xml`.
Make sure to also export any changes to their respective `.svg` (and keep their name) to update the `.md` file.

1
raw/raw.xml Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
render-flow.svg Normal file

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

После

Ширина:  |  Высота:  |  Размер: 45 KiB