Skip to content

Commit

Permalink
Merge pull request #29 from nitin42/3.x.x
Browse files Browse the repository at this point in the history
3.x.x
  • Loading branch information
jcgertig committed Jul 27, 2017
2 parents 20dc0be + dd3c00c commit e3dfe21
Show file tree
Hide file tree
Showing 16 changed files with 876 additions and 291 deletions.
46 changes: 43 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,14 @@ class App extends Component {

render() {
return (
<div style={{ display: "flex", justifyContent: "center", alignItems: "center", height: "100vh" }}>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "100vh"
}}
>
<Terminal
color='green'
backgroundColor='black'
Expand Down Expand Up @@ -222,7 +229,7 @@ Let's take an another example -
<img src="https://i.gyazo.com/ef2427464989b1ce14bc44bb4fc94689.gif" />
</p>

## Using plugins 🔥
## Using plugins 🔥 [WIP]

We have also developed a plugin system for the `<Terminal />` component which helps you develop custom plugins. Here is one example of plugin which creates a fake file system called [terminal-in-react-pseudo-file-system-plugin](https://github.com/jcgertig/terminal-in-react-pseudo-file-system-plugin).

Expand Down Expand Up @@ -323,6 +330,31 @@ You can mix and match

The value of the shortcut should be a command to run.


## Override the top bar buttons actionHandlers

Use the prop `actionHandlers`.

The object allows for 3 methods `handleClose`, `handleMaximise`, `handleMinimise`;

Each one is a function and will pass in the default method as the first param.
Any method not passed in will use the default.

```jsx
<Terminal
actionHandlers={{
handleClose: (toggleClose) => {
// do something on close
toggleClose();
},
handleMaximise: (toggleMaximise) => {
// do something on maximise
toggleMaximise();
}
}}
/>
```

## Customization

Use
Expand All @@ -331,6 +363,9 @@ Use
* prop `backgroundColor` to change the background.
* prop `barColor` to change the color of bar.
* prop `prompt` to change the prompt (`>`) color.
* prop `showActions` to change if the three circles are shown.
* prop `hideTopBar` to hide the top bar altogether.
* prop `allowTabs` to allow multiple tabs.

Follow me on Twitter [@NTulswani](https://twitter.com/NTulswani) for new updates and progress 😄

Expand All @@ -351,6 +386,11 @@ Follow me on Twitter [@NTulswani](https://twitter.com/NTulswani) for new updates
| **commandPassThrough** | function | null |
| **promptSymbol** | string | > |
| **plugins** | array | [ { name: '', load: new Plugin(), commands: {} descriptions: {} } ] |
| **startState** | string ['open', 'maximised', 'minimised', 'closed'] | 'open' |
| **showActions** | bool | true |
| **hideTopBar** | bool | false |
| **allowTabs** | bool | true |
| **actionHandlers** | object | undefined |

## Built-in commands

Expand All @@ -369,7 +409,7 @@ Follow me on Twitter [@NTulswani](https://twitter.com/NTulswani) for new updates

## You want a X feature

Sure! Check our [todolist](./todo.md) or create an issue and I will look into it.
Sure! Check our [todolist](./todo.md) or create an issue and I will look into it.

## Contributing

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "terminal-in-react",
"version": "3.3.3",
"version": "3.4.0",
"description": "A component for making a terminal in React",
"main": "lib/js/index.js",
"module": "src/index.js",
Expand Down
66 changes: 43 additions & 23 deletions src/js/components/Bar.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ class Bar extends Component {
static displayName = 'Bar';

static propTypes = {
style: PropTypes.object // eslint-disable-line
style: PropTypes.object, // eslint-disable-line
showActions: PropTypes.bool,
handleMinimise: PropTypes.func,
handleMaximise: PropTypes.func,
handleClose: PropTypes.func,
};

static defaultProps = {
style: {},
showActions: true,
};

static contextTypes = {
Expand All @@ -21,45 +26,60 @@ class Bar extends Component {

// Close the window
handleClose = () => {
this.context.toggleShow();
if (this.props.handleClose) {
this.props.handleClose(this.context.toggleShow);
} else {
this.context.toggleShow();
}
};

// Minimise the window
handleMinimise = () => {
this.context.toggleMinimize();
if (this.props.handleMinimise) {
this.props.handleMinimise(this.context.toggleMinimize);
} else {
this.context.toggleMinimize();
}
};

// Maximise the window
handleMaximise = () => {
this.context.toggleMaximise();
if (this.props.handleMaximise) {
this.props.handleMaximise(this.context.toggleMaximise);
} else {
this.context.toggleMaximise();
}
};

render() {
const { style, showActions } = this.props;
return (
<div
style={{
...this.props.style,
...style,
...(this.context.maximise ? { maxWidth: '100%' } : {}),
}}
className="terminal-top-bar adjust-bar"
className="terminal-top-bar"
>
<svg height="20" width="100">
<circle cx="24" cy="14" r="5" fill="red" onClick={this.handleClose} />
<circle
cx="44"
cy="14"
r="5"
fill="orange"
onClick={this.handleMinimise}
/>
<circle
cx="64"
cy="14"
r="5"
fill="green"
onClick={this.handleMaximise}
/>
</svg>
{ showActions && (
<svg height="20" width="100">
<circle cx="24" cy="14" r="5" fill="red" onClick={this.handleClose} />
<circle
cx="44"
cy="14"
r="5"
fill="orange"
onClick={this.handleMinimise}
/>
<circle
cx="64"
cy="14"
r="5"
fill="green"
onClick={this.handleMaximise}
/>
</svg>
) }
</div>
);
}
Expand Down
110 changes: 93 additions & 17 deletions src/js/components/Content.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,131 @@
import React, { Component } from 'react'; // eslint-disable-line
import PropTypes from 'prop-types';
import whatkey, { unprintableKeys } from 'whatkey';

class Content extends Component {
static displayName = 'Content';

static propTypes = {
id: PropTypes.string,
oldData: PropTypes.object, // eslint-disable-line
backgroundColor: PropTypes.objectOf(PropTypes.string),
output: PropTypes.arrayOf(PropTypes.element),
prompt: PropTypes.objectOf(PropTypes.string),
inputStyles: PropTypes.objectOf(PropTypes.string),
register: PropTypes.func,
handleChange: PropTypes.func,
handlerKeyPress: PropTypes.func.isRequired,
};

static defaultProps = {
oldData: {},
};

static contextTypes = {
symbol: PropTypes.string,
maximise: PropTypes.bool,
instances: PropTypes.array,
activeTab: PropTypes.string,
barShowing: PropTypes.bool,
tabsShowing: PropTypes.bool,
};

state = {
summary: [],
promptPrefix: '',
history: [],
historyCounter: 0,
input: [],
keyInputs: [],
};

componentWillMount = () => {
const data = this.context.instances.find(i => i.index === this.props.id);
if (data) {
this.setState(data.oldData);
}
};

componentDidMount = () => {
this.focusInput();
const data = this.context.instances.find(i => i.index === this.props.id);
this.unregister = this.props.register(this);
if (!data || Object.keys(data.oldData).length === 0) {
this.handleChange({ target: { value: 'show' }, key: 'Enter', dontShowCommand: true });
}
};

// Adjust scrolling
componentDidUpdate = () => {
if (this.inputWrapper !== null) this.inputWrapper.scrollIntoView(false);
if (this.inputWrapper !== null) {
this.inputWrapper.scrollIntoView(false);
}
};

componentWillUnmount() {
this.unregister(this.state);
}

focusInput = () => {
if (this.com !== null) this.com.focus();
if (this.com !== null) {
this.com.focus();
}
};

handleChange = (e) => {
this.props.handleChange(this, e);
}

handleKeyPress = (e) => {
this.props.handlerKeyPress(this, e, this.com);
}

handleOuterKeypress = (e) => {
const key = whatkey(e).key;
const actionKeys = ['up', 'down', 'left', 'right', 'enter'];
if (unprintableKeys.indexOf(key) < 0) {
if (document.activeElement !== this.com) {
this.com.focus();
this.com.value += whatkey(e).char;
}
} else if (actionKeys.indexOf(key) > -1) {
this.com.focus();
}
}

render() {
const {
output,
prompt,
inputStyles,
handleChange,
backgroundColor,
handlerKeyPress,
} = this.props;
const { symbol, maximise } = this.context;
const { prompt, inputStyles, backgroundColor, id } = this.props;
const { symbol, maximise, activeTab, barShowing, tabsShowing } = this.context;

if (id !== activeTab) {
return null;
}

const output = this.state.summary.map((content, i) => {
if (typeof content === 'string' && content.length === 0) {
return <div className="terminal-output-line" key={i}>&nbsp;</div>;
}
return <pre className="terminal-output-line" key={i}>{content}</pre>;
});

let toSubtract = 30;
if (!barShowing) {
toSubtract -= 30;
}
if (tabsShowing) {
toSubtract += 30;
}

return (
<div
className="terminal-container terminal-container-main"
style={{
...backgroundColor,
...(maximise
? { maxWidth: '100%', maxHeight: 'calc(100% - 30px)' }
? { maxWidth: '100%', maxHeight: `calc(100% - ${toSubtract}px)` }
: {}),
}}
onClick={this.focusInput}
tabIndex="0"
onKeyUp={this.handleOuterKeypress}
>
<div className="terminal-holder">
<div className="terminal-content">
Expand All @@ -61,15 +135,17 @@ class Content extends Component {
className="terminal-input"
ref={elm => (this.inputWrapper = elm)}
>
<span className="terminal-prompt" style={prompt}>{symbol}</span>
<span className="terminal-prompt" style={prompt}>
{this.state.promptPrefix + symbol}
</span>
<input
className="terminal-main-input"
style={inputStyles}
type="text"
tabIndex="-1"
ref={com => (this.com = com)}
onKeyPress={handleChange}
onKeyDown={e => handlerKeyPress(e, this.com)}
onKeyPress={this.handleChange}
onKeyDown={this.handleKeyPress}
/>
</div>
</div>
Expand Down
23 changes: 15 additions & 8 deletions src/js/components/Plugin.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
export default class Plugin {
constructor(name = '', version = '1.0.0') {
this.name = name;
this.version = version;
}
static displayName = '';
static version = '1.0.0';
static defaultData = '';

load = () => {};
static commands = {};
static descriptions = {};

afterLoad = () => {};
static defaultConfig = {};

getPublicMethods = () => ({});
constructor(api, config = Plugin.defaultConfig) {
this.api = api;
this.config = config;
this.commands = {};
this.descriptions = {};

readStdOut = () => true;
this.updateApi = newApi => (this.api = newApi);
this.getPublicMethods = () => ({});
this.readStdOut = () => true;
}
}
Loading

0 comments on commit e3dfe21

Please sign in to comment.