aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/actions/completion.js22
-rw-r--r--src/actions/index.js5
-rw-r--r--src/components/completion.js55
-rw-r--r--src/pages/completion.js27
-rw-r--r--src/pages/console.js120
-rw-r--r--src/reducers/completion.js61
6 files changed, 172 insertions, 118 deletions
diff --git a/src/actions/completion.js b/src/actions/completion.js
new file mode 100644
index 0000000..1ffb025
--- /dev/null
+++ b/src/actions/completion.js
@@ -0,0 +1,22 @@
+import actions from '../actions';
+
+const setItems = (groups) => {
+ return {
+ type: actions.COMPLETION_SET_ITEMS,
+ groups,
+ };
+};
+
+const selectNext = () => {
+ return {
+ type: actions.COMPLETION_SELECT_NEXT
+ };
+};
+
+const selectPrev = () => {
+ return {
+ type: actions.COMPLETION_SELECT_PREV
+ };
+};
+
+export { setItems, selectNext, selectPrev };
diff --git a/src/actions/index.js b/src/actions/index.js
index 7b79864..2aa28fa 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -9,4 +9,9 @@ export default {
INPUT_KEY_PRESS: 'input.key,press',
INPUT_CLEAR_KEYS: 'input.clear.keys',
INPUT_SET_KEYMAPS: 'input.set,keymaps',
+
+ // Completion
+ COMPLETION_SET_ITEMS: 'completion.set.items',
+ COMPLETION_SELECT_NEXT: 'completions.select.next',
+ COMPLETION_SELECT_PREV: 'completions.select.prev'
};
diff --git a/src/components/completion.js b/src/components/completion.js
new file mode 100644
index 0000000..e6ee0cb
--- /dev/null
+++ b/src/components/completion.js
@@ -0,0 +1,55 @@
+export default class Completion {
+ constructor(wrapper, store) {
+ this.wrapper = wrapper;
+ this.store = store;
+ }
+
+ update() {
+ let state = this.store.getState();
+
+ this.wrapper.innerHTML = '';
+
+ for (let i = 0; i < state.groups.length; ++i) {
+ let group = state.groups[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');
+ }
+ }
+ }
+ }
+
+ 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/pages/completion.js b/src/pages/completion.js
deleted file mode 100644
index 4c69afb..0000000
--- a/src/pages/completion.js
+++ /dev/null
@@ -1,27 +0,0 @@
-export default class Completion {
- constructor(completions) {
- if (typeof completions.length !== 'number') {
- throw new TypeError('completions does not have a length in number');
- }
- this.completions = completions;
- this.index = 0;
- }
-
- prev() {
- let length = this.completions.length;
- if (length === 0) {
- return null;
- }
- this.index = (this.index + length - 1) % length;
- return this.completions[this.index];
- }
-
- next() {
- if (this.completions.length === 0) {
- return null;
- }
- let item = this.completions[this.index];
- this.index = (this.index + 1) % this.completions.length;
- return item;
- }
-}
diff --git a/src/pages/console.js b/src/pages/console.js
index 31f2643..40e713e 100644
--- a/src/pages/console.js
+++ b/src/pages/console.js
@@ -1,61 +1,41 @@
import './console.scss';
-import Completion from './completion';
import messages from '../content/messages';
+import CompletionComponent from '../components/completion';
+import completionReducer from '../reducers/completion';
+import * as store from '../store';
+import * as completionActions from '../actions/completion';
+
+const completionStore = store.createStore(completionReducer);
+let completionComponent = null;
+
+window.addEventListener('load', () => {
+ let wrapper = document.querySelector('#vimvixen-console-completion');
+ completionComponent = new CompletionComponent(wrapper, completionStore);
+});
// TODO consider object-oriented
let prevValue = '';
-let completion = null;
let completionOrigin = '';
let prevState = {};
-const handleBlur = () => {
- return browser.runtime.sendMessage({
- type: messages.CONSOLE_BLURRED,
- });
-};
-
-const selectCompletion = (target) => {
- let container = window.document.querySelector('#vimvixen-console-completion');
- Array.prototype.forEach.call(container.children, (ele) => {
- if (!ele.classList.contains('vimvixen-console-completion-item')) {
- return;
- }
- if (ele === target) {
- ele.classList.add('vimvixen-completion-selected');
- } else {
- ele.classList.remove('vimvixen-completion-selected');
- }
- });
-};
-
-const completeNext = () => {
- if (!completion) {
- return;
- }
- let item = completion.next();
- if (!item) {
- return;
- }
+completionStore.subscribe(() => {
+ completionComponent.update();
+ let state = completionStore.getState();
let input = window.document.querySelector('#vimvixen-console-command-input');
- input.value = completionOrigin + ' ' + item[0].content;
- selectCompletion(item[1]);
-};
-
-const completePrev = () => {
- if (!completion) {
- return;
+ if (state.groupSelection >= 0) {
+ let item = state.groups[state.groupSelection].items[state.itemSelection];
+ input.value = completionOrigin + ' ' + item.content;
+ } else if (state.groups.length > 0) {
+ input.value = completionOrigin + ' ';
}
- let item = completion.prev();
- if (!item) {
- return;
- }
-
- let input = window.document.querySelector('#vimvixen-console-command-input');
- input.value = completionOrigin + ' ' + item[0].content;
+});
- selectCompletion(item[1]);
+const handleBlur = () => {
+ return browser.runtime.sendMessage({
+ type: messages.CONSOLE_BLURRED,
+ });
};
const handleKeydown = (e) => {
@@ -71,9 +51,9 @@ const handleKeydown = (e) => {
});
case KeyboardEvent.DOM_VK_TAB:
if (e.shiftKey) {
- completePrev();
+ completionStore.dispatch(completionActions.selectPrev());
} else {
- completeNext();
+ completionStore.dispatch(completionActions.selectNext());
}
e.stopPropagation();
e.preventDefault();
@@ -102,52 +82,10 @@ window.addEventListener('load', () => {
input.addEventListener('keyup', handleKeyup);
});
-const createCompletionTitle = (text) => {
- let li = document.createElement('li');
- li.className = 'vimvixen-console-completion-title';
- li.textContent = text;
- return li;
-};
-
-const createCompletionItem = (icon, caption, url) => {
- let captionEle = document.createElement('span');
- captionEle.className = 'vimvixen-console-completion-item-caption';
- captionEle.textContent = caption;
-
- let urlEle = document.createElement('span');
- urlEle.className = 'vimvixen-console-completion-item-url';
- urlEle.textContent = url;
-
- let li = document.createElement('li');
- li.style.backgroundImage = 'url(' + icon + ')';
- li.className = 'vimvixen-console-completion-item';
- li.append(captionEle);
- li.append(urlEle);
- return li;
-};
-
const updateCompletions = (completions) => {
- let completionsContainer =
- window.document.querySelector('#vimvixen-console-completion');
- let input = window.document.querySelector('#vimvixen-console-command-input');
-
- completionsContainer.innerHTML = '';
-
- let pairs = [];
-
- for (let group of completions) {
- let title = createCompletionTitle(group.name);
- completionsContainer.append(title);
+ completionStore.dispatch(completionActions.setItems(completions));
- for (let item of group.items) {
- let li = createCompletionItem(item.icon, item.caption, item.url);
- completionsContainer.append(li);
-
- pairs.push([item, li]);
- }
- }
-
- completion = new Completion(pairs);
+ let input = window.document.querySelector('#vimvixen-console-command-input');
completionOrigin = input.value.split(' ')[0];
};
diff --git a/src/reducers/completion.js b/src/reducers/completion.js
new file mode 100644
index 0000000..a706988
--- /dev/null
+++ b/src/reducers/completion.js
@@ -0,0 +1,61 @@
+import actions from '../actions';
+
+const defaultState = {
+ groupSelection: -1,
+ itemSelection: -1,
+ groups: [],
+};
+
+const nextSelection = (state) => {
+ if (state.groupSelection < 0) {
+ return [0, 0];
+ }
+
+ let group = state.groups[state.groupSelection];
+ if (state.groupSelection + 1 >= state.groups.length &&
+ state.itemSelection + 1 >= group.items.length) {
+ return [-1, -1];
+ }
+ if (state.itemSelection + 1 >= group.items.length) {
+ return [state.groupSelection + 1, 0];
+ }
+ return [state.groupSelection, state.itemSelection + 1];
+};
+
+const prevSelection = (state) => {
+ if (state.groupSelection < 0) {
+ return [0, 0];
+ }
+ if (state.groupSelection === 0 && state.itemSelection === 0) {
+ return [-1, -1];
+ } else if (state.itemSelection === 0) {
+ return [
+ state.groupSelection - 1,
+ state.groups[state.groupSelection - 1].items.length - 1
+ ];
+ }
+ return [state.groupSelection, state.itemSelection - 1];
+};
+
+export default function reducer(state = defaultState, action = {}) {
+ switch (action.type) {
+ case actions.COMPLETION_SET_ITEMS:
+ return Object.assign({}, state, {
+ groups: action.groups
+ });
+ case actions.COMPLETION_SELECT_NEXT: {
+ let next = nextSelection(state);
+ return Object.assign({}, state, {
+ groupSelection: next[0],
+ itemSelection: next[1],
+ });
+ }
+ case actions.COMPLETION_SELECT_PREV: {
+ let next = prevSelection(state);
+ return Object.assign({}, state, {
+ groupSelection: next[0],
+ itemSelection: next[1],
+ });
+ }
+ }
+}