Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@gondel/plugin-react

Package Overview
Dependencies
Maintainers
4
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@gondel/plugin-react - npm Package Compare versions

Comparing version 1.0.0 to 1.1.0

dist/GondelReactComponent.d.ts

11

CHANGELOG.md

@@ -6,2 +6,13 @@ # Change Log

# [1.1.0](https://github.com/namics/gondel/compare/v1.0.0...v1.1.0) (2020-01-16)
### Features
* **react plugin:** add async linkining support to the gondel react plugin ([b39ca5a](https://github.com/namics/gondel/commit/b39ca5a))
# [1.0.0](https://github.com/namics/gondel/compare/v0.1.0...v1.0.0) (2019-11-28)

@@ -8,0 +19,0 @@

2

dist/AppWrapper.d.ts

@@ -11,2 +11,2 @@ import React, { Component } from "react";

}
export declare function createRenderAbleAppWrapper<TConfig>(props: Props<TConfig>): JSX.Element;
export declare function createRenderableAppWrapper<TConfig>(props: Props<TConfig>): JSX.Element;

@@ -60,5 +60,5 @@ var __extends = (this && this.__extends) || (function () {

export { AppWrapper };
export function createRenderAbleAppWrapper(props) {
export function createRenderableAppWrapper(props) {
return React.createElement(AppWrapper, __assign({}, props));
}
//# sourceMappingURL=AppWrapper.js.map
/**
* This is a plugin which allows a simplified usage of gondel together with react
*/
import { GondelBaseComponent, GondelComponent } from "@gondel/core";
import React, { ComponentLifecycle, StatelessComponent, ComponentClass } from "react";
export declare class GondelReactComponent<State> extends GondelBaseComponent implements ComponentLifecycle<null, State> {
_setInternalState: (config: State) => void | undefined;
App?: StatelessComponent<Readonly<State>> | ComponentClass<Readonly<State>, any>;
state: Readonly<State>;
setState(state: Partial<State>): void;
constructor(ctx: HTMLElement, componentName: string);
/**
* Called immediately before mounting occurs, and before `Component#render`.
* Avoid introducing any side-effects or subscriptions in this method.
*/
componentWillMount?(): void;
/**
* Called immediately after a compoment is mounted. Setting state here will trigger re-rendering.
*/
componentDidMount?(): void;
/**
* Called when the component may be receiving new props.
* React may call this even if props have not changed, so be sure to compare new and existing
* props if you only want to handle changes.
*
* Calling `Component#setState` generally does not trigger this method.
*/
componentWillReceiveProps?(nextProps: Readonly<null>, nextContext: any): void;
/**
* Called to determine whether the change in props and state should trigger a re-render.
*
* `Component` always returns true.
* `PureComponent` implements a shallow comparison on props and state and returns true if any
* props or states have changed.
*
* If false is returned, `Component#render`, `componentWillUpdate`
* and `componentDidUpdate` will not be called.
*/
shouldComponentUpdate?(nextProps: Readonly<null>, nextState: Readonly<State>, nextContext: any): boolean;
/**
* Called immediately before rendering when new props or state is received. Not called for the initial render.
*
* Note: You cannot call `Component#setState` here.
*/
componentWillUpdate?(nextProps: Readonly<null>, nextState: Readonly<State>, nextContext: any): void;
/**
* Called immediately after updating occurs. Not called for the initial render.
*/
componentDidUpdate?(prevProps: Readonly<null>, prevState: Readonly<State>, prevContext: any): void;
/**
* Called immediately before a component is destroyed. Perform any necessary cleanup in this method, such as
* cancelled network requests, or cleaning up any DOM elements created in `componentDidMount`.
*/
componentWillUnmount?(): void;
/**
* Catches exceptions generated in descendant components. Unhandled exceptions will cause
* the entire component tree to unmount.
*/
componentDidCatch?(error: Error, errorInfo: React.ErrorInfo): void;
render(): any;
}
/** React hook to use Gondel components inside React */
export declare function useGondelComponent<TComponentType extends GondelComponent>(): readonly [(element: HTMLElement | null) => void, TComponentType | null];
export { useGondelComponent } from "./hooks";
export { GondelReactComponent } from "./GondelReactComponent";

@@ -1,118 +0,6 @@

var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
/**
* This is a plugin which allows a simplified usage of gondel together with react
*/
import { GondelBaseComponent, startComponents, stopComponents, getComponentByDomNode, hasMountedGondelComponent } from "@gondel/core";
import React, { useCallback, useRef, useState, useEffect } from "react";
import { createRenderAbleAppWrapper } from "./AppWrapper";
/**
* Returns true if the given object is promise like
*/
function isPromise(obj) {
return obj !== undefined && obj.then !== undefined;
}
var GondelReactComponent = /** @class */ (function (_super) {
__extends(GondelReactComponent, _super);
function GondelReactComponent(ctx, componentName) {
var _this = _super.call(this, ctx, componentName) || this;
// Overwrite the current start method
var originalStart = _this.start;
var ReactDOMPromise = import(
/* webpackPrefetch: true, webpackChunkName: 'ReactDom' */ "react-dom").then(function (ReactDOM) { return ReactDOM.default; });
var configScript = ctx.querySelector("script[type='text/json']");
_this.state = configScript ? JSON.parse(configScript.innerHTML) : {};
_this.start = function () {
var _this = this;
// Wait for the original start promise to allow lazy loading
var originalStartPromise = new Promise(function (resolve, reject) {
if (!originalStart) {
return resolve();
}
if (originalStart.length) {
return originalStart.call(_this, resolve, reject);
}
var result = originalStart.call(_this);
return isPromise(result) ? result.then(resolve, reject) : resolve(result);
});
// Render the app
var renderAppPromise = originalStartPromise
.then(function () { return ReactDOMPromise; })
.then(function (ReactDOM) {
// Render only if the app was not stopped
_this._stopped ||
ReactDOM.render(createRenderAbleAppWrapper({
children: _this.render.bind(_this),
onHasState: function (setInternalState) {
_this._setInternalState = setInternalState;
},
componentWillUnmount: function () {
delete _this._setInternalState;
_this.componentWillUnmount && _this.componentWillUnmount();
},
componentDidMount: _this.componentDidMount && _this.componentDidMount.bind(_this),
componentWillReceiveProps: _this.componentWillReceiveProps && _this.componentWillReceiveProps.bind(_this),
shouldComponentUpdate: _this.shouldComponentUpdate && _this.shouldComponentUpdate.bind(_this),
componentWillUpdate: _this.componentWillUpdate && _this.componentWillUpdate.bind(_this),
componentDidUpdate: _this.componentDidUpdate && _this.componentDidUpdate.bind(_this),
componentDidCatch: _this.componentDidCatch && _this.componentDidCatch.bind(_this),
config: _this.state
}), _this._ctx);
});
return renderAppPromise;
};
return _this;
}
GondelReactComponent.prototype.setState = function (state) {
this.state = Object.assign({}, this.state, state);
// Handover the state to react
// if the component was already rendered
if (this._setInternalState) {
this._setInternalState(this.state);
}
};
GondelReactComponent.prototype.render = function () {
if (this.App) {
return React.createElement(this.App, this.state);
}
throw new Error(this._componentName + " is missing a render method");
};
return GondelReactComponent;
}(GondelBaseComponent));
export { GondelReactComponent };
/** React hook to use Gondel components inside React */
export function useGondelComponent() {
var _a = useState(null), gondelInstance = _a[0], setGondelInstance = _a[1];
var ref = useRef();
var refFunction = useCallback(function (element) {
if (element) {
ref.current = element;
startComponents(element).then(function () {
setGondelInstance(hasMountedGondelComponent(element) ? getComponentByDomNode(element) : null);
});
}
}, []);
useEffect(function () {
// Cleanup on unmount
return function () {
var element = ref.current;
if (element) {
stopComponents(element);
ref.current = undefined;
}
};
}, []);
return [refFunction, gondelInstance];
}
export { useGondelComponent } from "./hooks";
export { GondelReactComponent } from "./GondelReactComponent";
//# sourceMappingURL=index.js.map
{
"name": "@gondel/plugin-react",
"version": "1.0.0",
"version": "1.1.0",
"description": "Gondel Plugin to boot react widgets and apps",

@@ -14,2 +14,3 @@ "bugs": "https://github.com/namics/gondel/issues",

"scripts": {
"test": "jest",
"build:clean": "rimraf dist",

@@ -29,3 +30,4 @@ "build:esm": "tsc --project ./tsconfig.json --declaration --esModuleInterop --declarationDir dist/ --sourceMap --outDir dist/",

"devDependencies": {
"@gondel/core": "^1.0.0",
"@gondel/core": "^1.1.0",
"jest": "24.9.0",
"npm-run-all": "4.1.5",

@@ -35,5 +37,28 @@ "react": "16.11.0",

"rimraf": "3.0.0",
"typescript": "3.6.2"
"typescript": "3.7.4"
},
"gitHead": "45edacbe40d1307cf4a7471a85554dba385b144d"
"jest": {
"globals": {
"ts-jest": {
"diagnostics": true
}
},
"transform": {
"^.+\\.tsx?": "ts-jest"
},
"testRegex": "\\.test\\.tsx?",
"collectCoverage": true,
"collectCoverageFrom": [
"src/**/*.{ts,tsx,js,jsx}"
],
"coverageDirectory": "coverage",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"json",
"node"
]
},
"gitHead": "f5265fead99d39b1b5fee5572e5216c1fcb0a34f"
}

@@ -7,3 +7,3 @@ # React Plugin

html
**HTML**

@@ -14,3 +14,3 @@ ```html

js
**JavaScript**

@@ -35,6 +35,7 @@ ```js

Most apps need some specific configuration e.g. Api enpoints.
The following pattern allows you to pass a basic configuration from the dom to your app.
Most apps need some specific configuration e.g. API enpoints or other settings.
The following pattern allows you to pass a basic configuration from the DOM to your application.
This guarantees us that we have the full flexibility to pass a configuration, so that it can get rendered by anyone (e.g. CMS).
html
**HTML**

@@ -48,3 +49,3 @@ ```html

js
**JavaScript**

@@ -67,10 +68,12 @@ ```js

## Component linking
## Lazy loading
It's also possible to link a gondel component to a react component without using a render method.
To load the javascript of your react widget only if the matching HTML Element is present you can use
the following pattern:
### Sync linking example
html
In the following example below the React app will be bundled into the same bundle (no code splitting).
**HTML**
```html

@@ -83,49 +86,60 @@ <div data-g-name="DemoWidget">

js
**JavaScript**
```js
import {GondelReactComponent} from '@gondel/plugin-react';
import {Component} from '@gondel/core';
import React from 'react';
import { GondelReactComponent } from '@gondel/plugin-react';
import { Component } from '@gondel/core';
import { App } from './App';
@Component('DemoWidget')
export class DemoWidget extends GondelReactComponent {
async start() {
this.App = await import('./App').App;
}
render(config) {
const App = this.App;
return
<App config={config} />
));
}
App = App;
}
```
### Lazy linking example (code splitting)
## Component linking
To only lazy load the JavaScript of your React widget if the matching
HTML Element is present, you can use the following pattern below which is called lazy linking:
It's also possible to link a gondel component to a react component without using a render method.
**HTML**
### Sync linking example
```html
<div data-g-name="DemoWidget">
<script type="text/json">{ "foo":"bar" }</script>
Loading..
</div>
```
**JavaScript**
```js
import {GondelReactComponent} from '@gondel/plugin-react';
import {Component} from '@gondel/core';
import {App} from './App';
import React from 'react';
import { GondelReactComponent } from '@gondel/plugin-react';
import { Component } from '@gondel/core';
@Component('DemoWidget')
export class DemoWidget extends GondelReactComponent {
App = App;
App = import('./App').then(({ App }) => App);
}
```
### Async linking example
### Async blocking linking example (code splitting)
You can use a async start method to lazy load a component and tell Gondel to wait until the JavaScript of the component has been loaded.
This will guarantee that the sync method of all Gondel components will be delayed until the React component was completely loaded.
**HTML**
```html
<div data-g-name="DemoWidget">
<script type="text/json">{ "foo":"bar" }</script>
Loading..
</div>
```
**JavaScript**
```js
import {GondelReactComponent} from '@gondel/plugin-react';
import {Component} from '@gondel/core';
import React from 'react';
import { GondelReactComponent } from '@gondel/plugin-react';
import { Component } from '@gondel/core';

@@ -135,3 +149,3 @@ @Component('DemoWidget')

async start() {
this.App = await import('./App').App;
this.App = (await import('./App')).App;
}

@@ -144,16 +158,32 @@ }

It is possible to manipulate the state inside a public method.
Initially the state is load from the script tag inside the components HTML markup.
In the following example below, Gondel would extract the initial state `{ theme: 'light' }`:
```js
import {GondelReactComponent} from '@gondel/plugin-react';
import {Component} from '@gondel/core';
import {App} from './App';
```html
<div data-g-name="DemoWidget">
<script type="text/json">{ "theme":"light" }</script>
Loading..
</div>
```
This initial state can be accessed inside the `GondelReactComponent` using `this.state`.
It is even possible to update the state of the component by calling the method `this.setState(...)`:
```tsx
import React from 'react';
import { GondelReactComponent } from '@gondel/plugin-react';
import { Component } from '@gondel/core';
const DemoApp = ({ theme }) => (
<h1 className={theme === 'dark' ? 'dark' : 'light'}>
Hello World
</h1>
);
@Component('DemoWidget')
export class DemoWidget extends GondelReactComponent<{counter: number}> {
App = App;
export class DemoWidget extends GondelReactComponent<{theme: 'light' | 'dark'}> {
App = DemoApp;
setCounter(counter: number) {
this.setState({counter})
setTheme(theme: 'light' | 'dark') {
this.setState({ theme });
}

@@ -163,8 +193,8 @@ }

With this public method it is now possible to set the counter using
get component by using [`getComponentByDomNode`](https://gondel.js.org/docs/api.html#getcomponentbydomnode-domnode-namespace-gondelbasecomponent):
In the example above we've created a public `setTheme` method which is now a public API for your React widget.
In combination with [`getComponentByDomNode`](https://gondel.js.org/docs/api.html#getcomponentbydomnode-domnode-namespace-gondelbasecomponent) it allows changing the state during runtime by external components:
```js
getComponentByDomNode(domElement).setCounter(10)
getComponentByDomNode(domElement).setTheme('dark')
```

@@ -174,4 +204,8 @@

The `useGondelComponent` hook allows to use a Gondel UI component like an accordion or button inside a react app.
The `useGondelComponent` hook allows us to use a Gondel UI component like an accordion or button inside a React app.
This can be really handy if you want to interop with your existing component markup inside of React.
### Example
```js

@@ -188,7 +222,8 @@ import { useGondelComponent } from '@gondel/plugin-react';

In addition to the `ref` object an instance of the gondel component is returned.
This allows controlling the Gondel component from react.
In addition to the `ref` object, an instance of the Gondel component gets returned.
This allows to fully control the Gondel component from the React code.
```js
// react component
**React component**
```tsx
import { useGondelComponent } from '@gondel/plugin-react';

@@ -199,13 +234,21 @@

return (
<button onClick={() => {
// Ensure that the gondelInstance is already initialized
if (gondelButtonInstance) {
// Execute a class method from the Gondel component
gondelButtonInstance.setIsEnabled(false);
}
}} ref={ref} data-g-name="Button"></button>
<button
ref={ref}
data-g-name="Button"
onClick={() => {
// Ensure that the gondelInstance is already initialized
if (gondelButtonInstance) {
// Execute a class method from the Gondel component
gondelButtonInstance.setIsEnabled(false);
}
}}>
Button text
</button>
);
};
```
// gondel component
**Gondel component**
```ts
import { Component, GondelBaseComponent } from '@gondel/core';

@@ -216,3 +259,3 @@

setIsEnabled(newState) {
if(newState) {
if (newState) {
this._ctx.removeAttribute('disabled');

@@ -219,0 +262,0 @@ } else {

/**
* This is a plugin which allows a simplified usage of gondel together with react
*/
import {
GondelBaseComponent,
GondelComponent,
startComponents,
stopComponents,
getComponentByDomNode,
hasMountedGondelComponent
} from "@gondel/core";
import React, {
ComponentLifecycle,
StatelessComponent,
ComponentClass,
useCallback,
useRef,
useState,
useEffect
} from "react";
import { createRenderAbleAppWrapper } from "./AppWrapper";
/**
* Returns true if the given object is promise like
*/
function isPromise<T>(obj: {} | Promise<T> | undefined | string | number): obj is Promise<T> {
return obj !== undefined && (<Promise<T>>obj).then !== undefined;
}
export class GondelReactComponent<State> extends GondelBaseComponent
implements ComponentLifecycle<null, State> {
_setInternalState: (config: State) => void | undefined;
App?: StatelessComponent<Readonly<State>> | ComponentClass<Readonly<State>, any>;
state: Readonly<State>;
setState(state: Partial<State>) {
this.state = Object.assign({}, this.state, state);
// Handover the state to react
// if the component was already rendered
if (this._setInternalState) {
this._setInternalState(this.state);
}
}
constructor(ctx: HTMLElement, componentName: string) {
super(ctx, componentName);
// Overwrite the current start method
const originalStart = (this as any).start;
const ReactDOMPromise = import(
/* webpackPrefetch: true, webpackChunkName: 'ReactDom' */ "react-dom"
).then(ReactDOM => ReactDOM.default);
const configScript = ctx.querySelector("script[type='text/json']");
this.state = configScript ? JSON.parse(configScript.innerHTML) : {};
(this as any).start = function(this: GondelReactComponent<State>) {
// Wait for the original start promise to allow lazy loading
const originalStartPromise = new Promise((resolve, reject) => {
if (!originalStart) {
return resolve();
}
if (originalStart.length) {
return originalStart.call(this, resolve, reject);
}
const result = originalStart.call(this);
return isPromise(result) ? result.then(resolve, reject) : resolve(result);
});
// Render the app
const renderAppPromise = originalStartPromise
.then(() => ReactDOMPromise)
.then(ReactDOM => {
// Render only if the app was not stopped
this._stopped ||
ReactDOM.render(
createRenderAbleAppWrapper({
children: this.render.bind(this),
onHasState: setInternalState => {
this._setInternalState = setInternalState;
},
componentWillUnmount: () => {
delete this._setInternalState;
this.componentWillUnmount && this.componentWillUnmount();
},
componentDidMount: this.componentDidMount && this.componentDidMount.bind(this),
componentWillReceiveProps:
this.componentWillReceiveProps && this.componentWillReceiveProps.bind(this),
shouldComponentUpdate:
this.shouldComponentUpdate && this.shouldComponentUpdate.bind(this),
componentWillUpdate:
this.componentWillUpdate && this.componentWillUpdate.bind(this),
componentDidUpdate: this.componentDidUpdate && this.componentDidUpdate.bind(this),
componentDidCatch: this.componentDidCatch && this.componentDidCatch.bind(this),
config: this.state
}),
this._ctx
);
});
return renderAppPromise;
};
}
/**
* Called immediately before mounting occurs, and before `Component#render`.
* Avoid introducing any side-effects or subscriptions in this method.
*/
componentWillMount?(): void;
/**
* Called immediately after a compoment is mounted. Setting state here will trigger re-rendering.
*/
componentDidMount?(): void;
/**
* Called when the component may be receiving new props.
* React may call this even if props have not changed, so be sure to compare new and existing
* props if you only want to handle changes.
*
* Calling `Component#setState` generally does not trigger this method.
*/
componentWillReceiveProps?(nextProps: Readonly<null>, nextContext: any): void;
/**
* Called to determine whether the change in props and state should trigger a re-render.
*
* `Component` always returns true.
* `PureComponent` implements a shallow comparison on props and state and returns true if any
* props or states have changed.
*
* If false is returned, `Component#render`, `componentWillUpdate`
* and `componentDidUpdate` will not be called.
*/
shouldComponentUpdate?(
nextProps: Readonly<null>,
nextState: Readonly<State>,
nextContext: any
): boolean;
/**
* Called immediately before rendering when new props or state is received. Not called for the initial render.
*
* Note: You cannot call `Component#setState` here.
*/
componentWillUpdate?(
nextProps: Readonly<null>,
nextState: Readonly<State>,
nextContext: any
): void;
/**
* Called immediately after updating occurs. Not called for the initial render.
*/
componentDidUpdate?(
prevProps: Readonly<null>,
prevState: Readonly<State>,
prevContext: any
): void;
/**
* Called immediately before a component is destroyed. Perform any necessary cleanup in this method, such as
* cancelled network requests, or cleaning up any DOM elements created in `componentDidMount`.
*/
componentWillUnmount?(): void;
/**
* Catches exceptions generated in descendant components. Unhandled exceptions will cause
* the entire component tree to unmount.
*/
componentDidCatch?(error: Error, errorInfo: React.ErrorInfo): void;
render(): any {
if (this.App) {
return React.createElement(this.App, this.state);
}
throw new Error(`${this._componentName} is missing a render method`);
}
}
/** React hook to use Gondel components inside React */
export function useGondelComponent<TComponentType extends GondelComponent>() {
const [gondelInstance, setGondelInstance] = useState<TComponentType | null>(null);
const ref = useRef<HTMLElement | undefined>();
const refFunction = useCallback((element: HTMLElement | null) => {
if (element) {
ref.current = element;
startComponents(element).then(() => {
setGondelInstance(
hasMountedGondelComponent(element) ? getComponentByDomNode<TComponentType>(element) : null
);
});
}
}, []);
useEffect(() => {
// Cleanup on unmount
return () => {
const element = ref.current;
if (element) {
stopComponents(element);
ref.current = undefined;
}
};
}, []);
return [refFunction, gondelInstance] as const;
}
export { useGondelComponent } from "./hooks";
export { GondelReactComponent } from "./GondelReactComponent";

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc