diff --git a/.gitignore b/.gitignore index 5b6e767..bb833e9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.log node_modules dist +.idea diff --git a/README.md b/README.md index e4aef2b..b0e106d 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ This renderer should currently be considered as experimental, is subject to chan * [Refs](#refs) * [Events](#events) * [Classes](#classes) + * [Using blessed forks](#using-blessed-forks) * [Roadmap](#roadmap) * [FAQ](#faq) * [Contribution](#contribution) @@ -236,6 +237,18 @@ class App extends Component { } ``` +### Using blessed forks + +Because [blessed](https://github.com/chjj/blessed) is not actively maintained in quite a while, you might want to use one of it's forks. To do that, import `createBlessedRenderer` function instead: + +``` +import React, {Component} from 'react'; +import blessed from 'neo-blessed'; +import {createBlessedRenderer} from 'react-blessed'; + +const render = createBlessedRenderer(blessed); +``` + ## Roadmap * Full support (meaning every tags and options should be handled by the renderer). diff --git a/examples/neo-blessed.jsx b/examples/neo-blessed.jsx new file mode 100644 index 0000000..bc48220 --- /dev/null +++ b/examples/neo-blessed.jsx @@ -0,0 +1,29 @@ +import React, {Component} from 'react'; +import blessed from 'neo-blessed'; +import {createBlessedRenderer} from '../src'; + +const render = createBlessedRenderer(blessed); + +class App extends Component { + render() { + return ( + + This example uses neo-blessed fork of blessed library. + + ); + } +} + +const screen = blessed.screen({ + autoPadding: true, + smartCSR: true, + title: 'react-blessed demo app' +}); + +screen.key(['escape', 'q', 'C-c'], function(ch, key) { + return process.exit(0); +}); + +const component = render(, screen); diff --git a/package.json b/package.json index 9ce6746..2c2cb90 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "babel-preset-stage-0": "^6.22.0", "babel-register": "^6.22.0", "blessed": "^0.1.81", + "neo-blessed": "^0.2.0", "mocha": "^4.0.1", "react": "^16.2.0" }, diff --git a/run.js b/run.js index 3f7d3ea..894181b 100644 --- a/run.js +++ b/run.js @@ -7,6 +7,7 @@ const examples = [ 'demo', 'form', 'framer', + 'neo-blessed', 'progressbar', 'remove', ]; diff --git a/src/fiber/fiber.js b/src/fiber/fiber.js index cc85c8e..d061af6 100644 --- a/src/fiber/fiber.js +++ b/src/fiber/fiber.js @@ -4,7 +4,6 @@ import type { HostConfig, Reconciler } from 'react-fiber-types'; const { debounce } = require('lodash'); -const blessed = require('blessed'); const ReactFiberReconciler : ( hostConfig: HostConfig<*, *, *, *, *, *, *, *> ) => Reconciler<*, *, *> = require('react-reconciler'); @@ -21,201 +20,203 @@ const { const emptyObject = {}; -type Instance = { - type: string, - props: Object, - _eventListener: Function, - _updating: boolean, - screen: typeof blessed.Screen, -}; - -const BlessedReconciler = ReactFiberReconciler({ - getRootHostContext(rootContainerInstance : Container) : HostContext { - return emptyObject; - }, - getChildHostContext(parentHostContext : HostContext, type: string) : HostContext { - return emptyObject; - }, - getPublicInstance(instance) { - return instance; - }, - - createInstance( - type : string, - props : Props, - rootContainerInstance : Container, - hostContext : HostContext, - internalInstanceHandle : Object - ) { - const {children, ...appliedProps} = solveClass(props); - const instance = blessed[type](appliedProps); - instance.props = props; - instance._eventListener = (...args) => eventListener(instance, ...args); - instance.on('event', instance._eventListener); - - return instance; - }, - - appendInitialChild( - parentInstance : Instance, - child : Instance | TextInstance - ) : void { - parentInstance.append(child); - }, - - finalizeInitialChildren( - instance : Instance, - type : string, - props : Props, - rootContainerInstance : Container - ) : boolean { - const {children, ...appliedProps} = solveClass(props); - update(instance, appliedProps); - instance.props = props; - return false; - }, - - prepareUpdate( - instance : Instance, - type : string, - oldProps : Props, - newProps : Props, - rootContainerInstance : Container, - hostContext : HostContext - ) : null | Array { - return solveClass(newProps); - }, - - shouldSetTextContent(props : Props) : boolean { - return false; - }, - - shouldDeprioritizeSubtree(type: string, props: Props): boolean { - return !!props.hidden; - }, - - now: Date.now, - - createTextInstance( - text : string, - rootContainerInstance : Container, - hostContext : HostContext, - internalInstanceHandle : OpaqueHandle - ) : TextInstance { - return blessed.text({content: text}); - }, +const createBlessedRenderer = function(blessed) { + type Instance = { + type: string, + props: Object, + _eventListener: Function, + _updating: boolean, + screen: typeof blessed.Screen, + }; + + const BlessedReconciler = ReactFiberReconciler({ + getRootHostContext(rootContainerInstance : Container) : HostContext { + return emptyObject; + }, + getChildHostContext(parentHostContext : HostContext, type: string) : HostContext { + return emptyObject; + }, + getPublicInstance(instance) { + return instance; + }, - scheduleDeferredCallback(a) { - throw new Error('Unimplemented'); - }, + createInstance( + type : string, + props : Props, + rootContainerInstance : Container, + hostContext : HostContext, + internalInstanceHandle : Object + ) { + const {children, ...appliedProps} = solveClass(props); + const instance = blessed[type](appliedProps); + instance.props = props; + instance._eventListener = (...args) => eventListener(instance, ...args); + instance.on('event', instance._eventListener); - prepareForCommit() { - // noop - }, + return instance; + }, - resetAfterCommit() { - // noop - }, + appendInitialChild( + parentInstance : Instance, + child : Instance | TextInstance + ) : void { + parentInstance.append(child); + }, - mutation: { - commitMount( + finalizeInitialChildren( instance : Instance, type : string, - newProps : Props, - internalInstanceHandle : Object - ) { - throw new Error('commitMount not implemented. Please post a reproducible use case that calls this method at https://github.com/Yomguithereal/react-blessed/issues/new'); - instance.screen.debouncedRender(); - // noop + props : Props, + rootContainerInstance : Container + ) : boolean { + const {children, ...appliedProps} = solveClass(props); + update(instance, appliedProps); + instance.props = props; + return false; }, - commitUpdate( + prepareUpdate( instance : Instance, - updatePayload : Array, type : string, oldProps : Props, newProps : Props, - internalInstanceHandle : Object, - ) : void { - instance._updating = true; - update(instance, updatePayload); - // update event handler pointers - instance.props = newProps; - instance._updating = false; - instance.screen.debouncedRender(); + rootContainerInstance : Container, + hostContext : HostContext + ) : null | Array { + return solveClass(newProps); }, - commitTextUpdate( - textInstance : TextInstance, - oldText : string, - newText : string - ) : void { - textInstance.setContent(newText); - textInstance.screen.debouncedRender(); + shouldSetTextContent(props : Props) : boolean { + return false; }, - appendChild( - parentInstance : Instance | Container, - child : Instance | TextInstance - ) : void { - parentInstance.append(child); + shouldDeprioritizeSubtree(type: string, props: Props): boolean { + return !!props.hidden; }, - appendChildToContainer( - parentInstance : Instance | Container, - child : Instance | TextInstance - ) : void { - parentInstance.append(child); - }, + now: Date.now, - insertBefore( - parentInstance : Instance | Container, - child : Instance | TextInstance, - beforeChild : Instance | TextInstance - ) : void { - // pretty sure everything is absolutely positioned so insertBefore ~= append - parentInstance.append(child); + createTextInstance( + text : string, + rootContainerInstance : Container, + hostContext : HostContext, + internalInstanceHandle : OpaqueHandle + ) : TextInstance { + return blessed.text({content: text}); }, - insertInContainerBefore( - parentInstance : Instance | Container, - child : Instance | TextInstance, - beforeChild : Instance | TextInstance - ) : void { - // pretty sure everything is absolutely positioned so insertBefore ~= append - parentInstance.append(child); + scheduleDeferredCallback(a) { + throw new Error('Unimplemented'); }, - removeChild( - parentInstance : Instance | Container, - child : Instance | TextInstance - ) : void { - parentInstance.remove(child); - child.off('event', child._eventListener); - child.destroy(); + prepareForCommit() { + // noop }, - removeChildFromContainer( - parentInstance : Instance | Container, - child : Instance | TextInstance - ) : void { - parentInstance.remove(child); - child.off('event', child._eventListener); - child.destroy(); + resetAfterCommit() { + // noop }, - resetTextContent(instance : Instance) : void { - instance.setContent(''); + mutation: { + commitMount( + instance : Instance, + type : string, + newProps : Props, + internalInstanceHandle : Object + ) { + throw new Error('commitMount not implemented. Please post a reproducible use case that calls this method at https://github.com/Yomguithereal/react-blessed/issues/new'); + instance.screen.debouncedRender(); + // noop + }, + + commitUpdate( + instance : Instance, + updatePayload : Array, + type : string, + oldProps : Props, + newProps : Props, + internalInstanceHandle : Object, + ) : void { + instance._updating = true; + update(instance, updatePayload); + // update event handler pointers + instance.props = newProps; + instance._updating = false; + instance.screen.debouncedRender(); + }, + + commitTextUpdate( + textInstance : TextInstance, + oldText : string, + newText : string + ) : void { + textInstance.setContent(newText); + textInstance.screen.debouncedRender(); + }, + + appendChild( + parentInstance : Instance | Container, + child : Instance | TextInstance + ) : void { + parentInstance.append(child); + }, + + appendChildToContainer( + parentInstance : Instance | Container, + child : Instance | TextInstance + ) : void { + parentInstance.append(child); + }, + + insertBefore( + parentInstance : Instance | Container, + child : Instance | TextInstance, + beforeChild : Instance | TextInstance + ) : void { + // pretty sure everything is absolutely positioned so insertBefore ~= append + parentInstance.append(child); + }, + + insertInContainerBefore( + parentInstance : Instance | Container, + child : Instance | TextInstance, + beforeChild : Instance | TextInstance + ) : void { + // pretty sure everything is absolutely positioned so insertBefore ~= append + parentInstance.append(child); + }, + + removeChild( + parentInstance : Instance | Container, + child : Instance | TextInstance + ) : void { + parentInstance.remove(child); + child.off('event', child._eventListener); + child.destroy(); + }, + + removeChildFromContainer( + parentInstance : Instance | Container, + child : Instance | TextInstance + ) : void { + parentInstance.remove(child); + child.off('event', child._eventListener); + child.destroy(); + }, + + resetTextContent(instance : Instance) : void { + instance.setContent(''); + }, }, - }, - useSyncScheduling: true, -}); + useSyncScheduling: true, + }); -BlessedReconciler.injectIntoDevTools(injectIntoDevToolsConfig); + BlessedReconciler.injectIntoDevTools(injectIntoDevToolsConfig); -module.exports = { - render(element, screen, callback) { + const roots = new Map(); + + return function render(element, screen, callback) { let root = roots.get(screen); if (!root) { root = BlessedReconciler.createContainer(screen); @@ -229,6 +230,13 @@ module.exports = { screen.debouncedRender(); return BlessedReconciler.getPublicRootInstance(root); } -}; +} -const roots = new Map(); +module.exports = { + render: function render(element, screen, callback) { + const blessed = require('blessed'); + const renderer = createBlessedRenderer(blessed); + return renderer(element, screen, callback); + }, + createBlessedRenderer: createBlessedRenderer +}; diff --git a/yarn.lock b/yarn.lock index a372134..63df409 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1473,6 +1473,10 @@ nan@^2.3.0: version "2.8.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" +neo-blessed@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/neo-blessed/-/neo-blessed-0.2.0.tgz#30f9495fdd104494402b62c6273a9c9b82de4f2b" + node-fetch@^1.0.1: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"