aboutsummaryrefslogtreecommitdiff
path: root/src/console
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2018-11-17 12:29:32 +0900
committerShin'ya Ueoka <ueokande@i-beam.org>2018-11-17 12:29:32 +0900
commit1fb5f433056e54aecc542fe1f4f684ada047032e (patch)
treeb867c8cab1f56477ae0dfefa5b5c86f5b181b9a5 /src/console
parent97787c773f0d1b5e80beb438246b5a693dbd08aa (diff)
Use preact in console
Diffstat (limited to 'src/console')
-rw-r--r--src/console/components/completion.js65
-rw-r--r--src/console/components/console.js175
-rw-r--r--src/console/components/console.jsx129
-rw-r--r--src/console/components/console.scss (renamed from src/console/site.scss)10
-rw-r--r--src/console/components/console/completion.jsx56
-rw-r--r--src/console/components/console/input.jsx32
-rw-r--r--src/console/components/console/message.jsx18
-rw-r--r--src/console/index.html12
-rw-r--r--src/console/index.jsx (renamed from src/console/index.js)16
9 files changed, 247 insertions, 266 deletions
diff --git a/src/console/components/completion.js b/src/console/components/completion.js
deleted file mode 100644
index a49a221..0000000
--- a/src/console/components/completion.js
+++ /dev/null
@@ -1,65 +0,0 @@
-export default class Completion {
- constructor(wrapper, store) {
- this.wrapper = wrapper;
- this.store = store;
- this.prevState = {};
-
- store.subscribe(() => {
- this.update();
- });
- }
-
- update() {
- let state = this.store.getState();
- if (JSON.stringify(this.prevState) === JSON.stringify(state)) {
- return;
- }
-
- this.wrapper.innerHTML = '';
-
- for (let i = 0; i < state.completions.length; ++i) {
- let group = state.completions[i];
- let title = this.createCompletionTitle(group.name);
- this.wrapper.append(title);
-
- for (let j = 0; j < group.items.length; ++j) {
- let item = group.items[j];
- let li = this.createCompletionItem(item.icon, item.caption, item.url);
- this.wrapper.append(li);
-
- if (i === state.groupSelection && j === state.itemSelection) {
- li.classList.add('vimvixen-completion-selected');
- }
- }
- }
-
- this.prevState = state;
- }
-
- createCompletionTitle(text) {
- let doc = this.wrapper.ownerDocument;
- let li = doc.createElement('li');
- li.className = 'vimvixen-console-completion-title';
- li.textContent = text;
- return li;
- }
-
- createCompletionItem(icon, caption, url) {
- let doc = this.wrapper.ownerDocument;
-
- let captionEle = doc.createElement('span');
- captionEle.className = 'vimvixen-console-completion-item-caption';
- captionEle.textContent = caption;
-
- let urlEle = doc.createElement('span');
- urlEle.className = 'vimvixen-console-completion-item-url';
- urlEle.textContent = url;
-
- let li = doc.createElement('li');
- li.style.backgroundImage = 'url(' + icon + ')';
- li.className = 'vimvixen-console-completion-item';
- li.append(captionEle);
- li.append(urlEle);
- return li;
- }
-}
diff --git a/src/console/components/console.js b/src/console/components/console.js
deleted file mode 100644
index 4fc8a53..0000000
--- a/src/console/components/console.js
+++ /dev/null
@@ -1,175 +0,0 @@
-import * as consoleActions from 'console/actions/console';
-
-const inputShownMode = (state) => {
- return ['command', 'find'].includes(state.mode);
-};
-
-export default class ConsoleComponent {
- constructor(wrapper, store) {
- this.wrapper = wrapper;
- this.store = store;
- this.prevMode = '';
-
- let doc = this.wrapper.ownerDocument;
- let input = doc.querySelector('#vimvixen-console-command-input');
-
- input.addEventListener('blur', this.onBlur.bind(this));
- input.addEventListener('keydown', this.onKeyDown.bind(this));
- input.addEventListener('input', this.onInput.bind(this));
-
- store.subscribe(() => {
- this.update();
- });
- this.update();
- }
-
- onBlur() {
- let state = this.store.getState();
- if (state.mode === 'command' || state.mode === 'find') {
- return this.store.dispatch(consoleActions.hideCommand());
- }
- }
-
- doEnter(e) {
- e.stopPropagation();
- e.preventDefault();
-
- let state = this.store.getState();
- let value = e.target.value;
- if (state.mode === 'command') {
- return this.store.dispatch(consoleActions.enterCommand(value));
- } else if (state.mode === 'find') {
- return this.store.dispatch(consoleActions.enterFind(value));
- }
- }
-
- selectNext(e) {
- this.store.dispatch(consoleActions.completionNext());
- e.stopPropagation();
- e.preventDefault();
- }
-
- selectPrev(e) {
- this.store.dispatch(consoleActions.completionPrev());
- e.stopPropagation();
- e.preventDefault();
- }
-
- onKeyDown(e) {
- if (e.keyCode === KeyboardEvent.DOM_VK_ESCAPE && e.ctrlKey) {
- this.store.dispatch(consoleActions.hideCommand());
- }
- switch (e.keyCode) {
- case KeyboardEvent.DOM_VK_ESCAPE:
- return this.store.dispatch(consoleActions.hideCommand());
- case KeyboardEvent.DOM_VK_RETURN:
- return this.doEnter(e);
- case KeyboardEvent.DOM_VK_TAB:
- if (e.shiftKey) {
- this.store.dispatch(consoleActions.completionPrev());
- } else {
- this.store.dispatch(consoleActions.completionNext());
- }
- e.stopPropagation();
- e.preventDefault();
- break;
- case KeyboardEvent.DOM_VK_OPEN_BRACKET:
- if (e.ctrlKey) {
- return this.store.dispatch(consoleActions.hideCommand());
- }
- break;
- case KeyboardEvent.DOM_VK_M:
- if (e.ctrlKey) {
- return this.doEnter(e);
- }
- break;
- case KeyboardEvent.DOM_VK_N:
- if (e.ctrlKey) {
- this.selectNext(e);
- }
- break;
- case KeyboardEvent.DOM_VK_P:
- if (e.ctrlKey) {
- this.selectPrev(e);
- }
- break;
- }
- }
-
- onInput(e) {
- let state = this.store.getState();
- let text = e.target.value;
- this.store.dispatch(consoleActions.setConsoleText(text));
- if (state.mode === 'command') {
- this.store.dispatch(consoleActions.getCompletions(text));
- }
- }
-
- onInputShown(state) {
- let doc = this.wrapper.ownerDocument;
- let input = doc.querySelector('#vimvixen-console-command-input');
-
- input.focus();
- window.focus();
-
- if (state.mode === 'command') {
- let text = state.consoleText;
- input.value = text;
- this.store.dispatch(consoleActions.getCompletions(text));
- }
- }
-
- update() {
- let state = this.store.getState();
-
- this.updateMessage(state);
- this.updateCommand(state);
- this.updatePrompt(state);
-
- if (this.prevMode !== state.mode && inputShownMode(state)) {
- this.onInputShown(state);
- }
- this.prevMode = state.mode;
- }
-
- updateMessage(state) {
- let doc = this.wrapper.ownerDocument;
- let box = doc.querySelector('.vimvixen-console-message');
- let display = 'none';
- let classList = ['vimvixen-console-message'];
-
- if (state.mode === 'error' || state.mode === 'info') {
- display = 'block';
- classList.push('vimvixen-console-' + state.mode);
- }
-
- box.className = classList.join(' ');
- box.style.display = display;
- box.textContent = state.messageText;
- }
-
- updateCommand(state) {
- let doc = this.wrapper.ownerDocument;
- let command = doc.querySelector('#vimvixen-console-command');
- let input = doc.querySelector('#vimvixen-console-command-input');
-
- let display = 'none';
- if (inputShownMode(state)) {
- display = 'block';
- }
-
- command.style.display = display;
- input.value = state.consoleText;
- }
-
- updatePrompt(state) {
- let classList = ['vimvixen-console-command-prompt'];
- if (inputShownMode(state)) {
- classList.push('prompt-' + state.mode);
- }
-
- let doc = this.wrapper.ownerDocument;
- let ele = doc.querySelector('.vimvixen-console-command-prompt');
- ele.className = classList.join(' ');
- }
-}
diff --git a/src/console/components/console.jsx b/src/console/components/console.jsx
new file mode 100644
index 0000000..70cbb4e
--- /dev/null
+++ b/src/console/components/console.jsx
@@ -0,0 +1,129 @@
+import './console.scss';
+import { connect } from 'preact-redux';
+import { Component, h } from 'preact';
+import Input from './console/input';
+import Completion from './console/completion';
+import Message from './console/message';
+import * as consoleActions from '../../console/actions/console';
+
+class ConsoleComponent extends Component {
+ onBlur() {
+ if (this.props.mode === 'command' || this.props.mode === 'find') {
+ return this.context.store.dispatch(consoleActions.hideCommand());
+ }
+ }
+
+ doEnter(e) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ let value = e.target.value;
+ if (this.props.mode === 'command') {
+ return this.context.store.dispatch(consoleActions.enterCommand(value));
+ } else if (this.props.mode === 'find') {
+ return this.context.store.dispatch(consoleActions.enterFind(value));
+ }
+ }
+
+ selectNext(e) {
+ this.context.store.dispatch(consoleActions.completionNext());
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ selectPrev(e) {
+ this.context.store.dispatch(consoleActions.completionPrev());
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ onKeyDown(e) {
+ if (e.keyCode === KeyboardEvent.DOM_VK_ESCAPE && e.ctrlKey) {
+ this.context.store.dispatch(consoleActions.hideCommand());
+ }
+ switch (e.keyCode) {
+ case KeyboardEvent.DOM_VK_ESCAPE:
+ return this.context.store.dispatch(consoleActions.hideCommand());
+ case KeyboardEvent.DOM_VK_RETURN:
+ return this.doEnter(e);
+ case KeyboardEvent.DOM_VK_TAB:
+ if (e.shiftKey) {
+ this.context.store.dispatch(consoleActions.completionPrev());
+ } else {
+ this.context.store.dispatch(consoleActions.completionNext());
+ }
+ e.stopPropagation();
+ e.preventDefault();
+ break;
+ case KeyboardEvent.DOM_VK_OPEN_BRACKET:
+ if (e.ctrlKey) {
+ return this.context.store.dispatch(consoleActions.hideCommand());
+ }
+ break;
+ case KeyboardEvent.DOM_VK_M:
+ if (e.ctrlKey) {
+ return this.doEnter(e);
+ }
+ break;
+ case KeyboardEvent.DOM_VK_N:
+ if (e.ctrlKey) {
+ this.selectNext(e);
+ }
+ break;
+ case KeyboardEvent.DOM_VK_P:
+ if (e.ctrlKey) {
+ this.selectPrev(e);
+ }
+ break;
+ }
+ }
+
+ onInput(e) {
+ let text = e.target.value;
+ this.context.store.dispatch(consoleActions.setConsoleText(text));
+ if (this.props.mode === 'command') {
+ this.context.store.dispatch(consoleActions.getCompletions(text));
+ }
+ }
+
+
+ componentDidUpdate(prevProps) {
+ if (!this.input) {
+ return;
+ }
+ if (prevProps.mode !== 'command' && this.props.mode === 'command') {
+ this.context.store.dispatch(
+ consoleActions.getCompletions(this.props.consoleText));
+ this.input.focus();
+ } else if (prevProps.mode !== 'find' && this.props.mode === 'find') {
+ this.input.focus();
+ }
+ }
+
+
+ render() {
+ switch (this.props.mode) {
+ case 'command':
+ case 'find':
+ return <div className='vimvixen-console-command-wrapper'>
+ <Completion />
+ <Input
+ ref={(c) => { this.input = c; }}
+ mode={this.props.mode}
+ onBlur={this.onBlur.bind(this)}
+ onKeyDown={this.onKeyDown.bind(this)}
+ onInput={this.onInput.bind(this)}
+ value={this.props.consoleText}
+ />
+ </div>;
+ case 'info':
+ case 'error':
+ return <Message mode={ this.props.mode } >
+ { this.props.messageText }
+ </Message>;
+ }
+ }
+}
+
+const mapStateToProps = state => state;
+export default connect(mapStateToProps)(ConsoleComponent);
diff --git a/src/console/site.scss b/src/console/components/console.scss
index b9b1016..c0b9b12 100644
--- a/src/console/site.scss
+++ b/src/console/components/console.scss
@@ -89,18 +89,10 @@ body {
background-color: white;
display: flex;
- &-prompt:before {
+ &-prompt {
@include consoole-font;
}
- &-prompt.prompt-command:before {
- content: ':';
- }
-
- &-prompt.prompt-find:before {
- content: '/';
- }
-
&-input {
border: none;
flex-grow: 1;
diff --git a/src/console/components/console/completion.jsx b/src/console/components/console/completion.jsx
new file mode 100644
index 0000000..c60543b
--- /dev/null
+++ b/src/console/components/console/completion.jsx
@@ -0,0 +1,56 @@
+import { Component, h } from 'preact';
+import { connect } from 'preact-redux';
+
+const CompletionTitle = (props) => {
+ return <li className='vimvixen-console-completion-title' >{props.title}</li>;
+};
+
+const CompletionItem = (props) => {
+ let className = 'vimvixen-console-completion-item';
+ if (props.highlight) {
+ className += ' vimvixen-completion-selected';
+ }
+ return <li
+ className={className}
+ style={{ backgroundImage: 'url(' + props.icon + ')' }}
+ >
+ <span
+ className='vimvixen-console-completion-item-caption'
+ >{props.caption}</span>
+ <span
+ className='vimvixen-console-completion-item-url'
+ >{props.url}</span>
+ </li>;
+};
+
+
+class CompletionComponent extends Component {
+ render() {
+ let eles = [];
+ for (let i = 0; i < this.props.completions.length; ++i) {
+ let group = this.props.completions[i];
+ eles.push(<CompletionTitle title={ group.name }/>);
+ for (let j = 0; j < group.items.length; ++j) {
+ let item = group.items[j];
+ let selected =
+ i === this.props.groupSelection &&
+ j === this.props.itemSelection;
+ eles.push(<CompletionItem
+ icon={item.icon}
+ caption={item.caption}
+ url={item.url}
+ highlight={selected}
+ / >);
+ }
+ }
+
+ return (
+ <ul className='vimvixen-console-completion'>
+ { eles }
+ </ul>
+ );
+ }
+}
+
+const mapStateToProps = state => state;
+export default connect(mapStateToProps)(CompletionComponent);
diff --git a/src/console/components/console/input.jsx b/src/console/components/console/input.jsx
new file mode 100644
index 0000000..d59e6e7
--- /dev/null
+++ b/src/console/components/console/input.jsx
@@ -0,0 +1,32 @@
+import { Component, h } from 'preact';
+
+export default class InputComponent extends Component {
+ focus() {
+ this.input.focus();
+ }
+
+ render() {
+ let prompt = '';
+ if (this.props.mode === 'command') {
+ prompt = ':';
+ } else if (this.props.mode === 'find') {
+ prompt = '/';
+ }
+
+ return (
+ <div className='vimvixen-console-command'>
+ <i className='vimvixen-console-command-prompt'>
+ { prompt }
+ </i>
+ <input
+ className='vimvixen-console-command-input'
+ ref={(c) => { this.input = c; }}
+ onBlur={this.props.onBlur}
+ onKeyDown={this.props.onKeyDown}
+ onInput={this.props.onInput}
+ value={this.props.value}
+ />
+ </div>
+ );
+ }
+}
diff --git a/src/console/components/console/message.jsx b/src/console/components/console/message.jsx
new file mode 100644
index 0000000..126687e
--- /dev/null
+++ b/src/console/components/console/message.jsx
@@ -0,0 +1,18 @@
+import { h } from 'preact';
+
+export default function Message(props) {
+ switch (props.mode) {
+ case 'error':
+ return (
+ <p className='vimvixen-console-message vimvixen-console-error'>
+ { props.text }
+ </p>
+ );
+ case 'info':
+ return (
+ <p className='vimvixen-console-message vimvixen-console-info'>
+ { props.children }
+ </p>
+ );
+ }
+}
diff --git a/src/console/index.html b/src/console/index.html
index e049b5e..5c1e99c 100644
--- a/src/console/index.html
+++ b/src/console/index.html
@@ -5,15 +5,5 @@
<title>VimVixen console</title>
<script src='console.js'></script>
</head>
- <body class='vimvixen-console'>
- <p class='vimvixen-console-message'></p>
- <div id='vimvixen-console-command' class='vimvixen-console-command-wrapper'>
- <ul id='vimvixen-console-completion' class='vimvixen-console-completion'></ul>
- <div class='vimvixen-console-command'>
- <i class='vimvixen-console-command-prompt'></i><input
- id='vimvixen-console-command-input'
- class='vimvixen-console-command-input'></input>
- </div>
- </div>
- </body>
+ <body class='vimvixen-console'></body>
</html>
diff --git a/src/console/index.js b/src/console/index.jsx
index 8724a44..c0d1807 100644
--- a/src/console/index.js
+++ b/src/console/index.jsx
@@ -1,21 +1,25 @@
-import './site.scss';
import messages from 'shared/messages';
-import CompletionComponent from 'console/components/completion';
-import ConsoleComponent from 'console/components/console';
import reducers from 'console/reducers';
import { createStore, applyMiddleware } from 'redux';
import promise from 'redux-promise';
import * as consoleActions from 'console/actions/console';
+import { Provider } from 'preact-redux';
+import Console from './components/console';
+
+import { render, h } from 'preact';
+
const store = createStore(
reducers,
applyMiddleware(promise),
);
window.addEventListener('load', () => {
- let wrapper = document.querySelector('#vimvixen-console-completion');
- new CompletionComponent(wrapper, store); // eslint-disable-line no-new
- new ConsoleComponent(document.body, store); // eslint-disable-line no-new
+ render(
+ <Provider store={store} >
+ <Console></Console>
+ </Provider>,
+ document.body);
});
const onMessage = (message) => {