aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2017-10-09 17:35:10 +0900
committerGitHub <noreply@github.com>2017-10-09 17:35:10 +0900
commit447466808f484d4baa6b285f2dbcaf1920db5498 (patch)
treeaba110eb78b4ce3eb6cefb8100f167e17a23fcc3
parent892eb8a6a6d9080213f461f19a8b8435a6482237 (diff)
parent805d1395fc869235f079438b5b4884a521c0230e (diff)
Merge pull request #27 from ueokande/react-settings
Use React in settings
-rw-r--r--.eslintrc9
-rw-r--r--package-lock.json370
-rw-r--r--package.json7
-rw-r--r--src/background/actions/settings.js0
-rw-r--r--src/background/components/background.js12
-rw-r--r--src/content/actions/index.js5
-rw-r--r--src/content/actions/input.js9
-rw-r--r--src/content/components/keymapper.js16
-rw-r--r--src/content/index.js4
-rw-r--r--src/content/reducers/index.js3
-rw-r--r--src/content/reducers/input.js5
-rw-r--r--src/settings/actions/setting.js16
-rw-r--r--src/settings/components/index.jsx91
-rw-r--r--src/settings/components/setting.js45
-rw-r--r--src/settings/components/site.scss (renamed from src/settings/site.scss)2
-rw-r--r--src/settings/index.html10
-rw-r--r--src/settings/index.js15
-rw-r--r--src/settings/index.jsx18
-rw-r--r--src/settings/reducers/setting.js12
-rw-r--r--src/shared/default-settings.js1
-rw-r--r--src/shared/store/provider.jsx18
-rw-r--r--test/settings/reducers/setting.test.js12
-rw-r--r--webpack.config.js6
23 files changed, 551 insertions, 135 deletions
diff --git a/.eslintrc b/.eslintrc
index 216b847..d244c8f 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -5,13 +5,14 @@
"browser" : true,
"webextensions": true
},
+ "plugins": ["react"],
"parser": "babel-eslint",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
}
},
- "extends": [ "eslint:all" ],
+ "extends": [ "eslint:all", "plugin:react/recommended" ],
"rules": {
"array-bracket-newline": ["error", { "multiline": true }],
"array-element-newline": "off",
@@ -27,6 +28,7 @@
"function-paren-newline": "off",
"id-length": "off",
"indent": ["error", 2],
+ "jsx-quotes": ["error", "prefer-single"],
"max-statements": ["error", 15],
"multiline-ternary": "off",
"newline-after-var": "off",
@@ -56,6 +58,9 @@
"sort-imports": "off",
"sort-keys": "off",
"sort-vars": "off",
- "space-before-function-paren": ["error", "never"]
+ "space-before-function-paren": ["error", "never"],
+
+ "react/jsx-indent": ["error", 2],
+ "react/prop-types": "off",
}
}
diff --git a/package-lock.json b/package-lock.json
index 7fbdd22..d7a09f4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -180,6 +180,16 @@
"integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
"dev": true
},
+ "array-includes": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
+ "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
+ "dev": true,
+ "requires": {
+ "define-properties": "1.1.2",
+ "es-abstract": "1.9.0"
+ }
+ },
"array-slice": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz",
@@ -219,6 +229,12 @@
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
"dev": true
},
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+ "dev": true
+ },
"asn1": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
@@ -399,6 +415,47 @@
"trim-right": "1.0.1"
}
},
+ "babel-helper-builder-react-jsx": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz",
+ "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "esutils": "2.0.2"
+ },
+ "dependencies": {
+ "babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+ "dev": true,
+ "requires": {
+ "core-js": "2.5.0",
+ "regenerator-runtime": "0.11.0"
+ }
+ },
+ "babel-types": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "esutils": "2.0.2",
+ "lodash": "4.17.4",
+ "to-fast-properties": "1.0.3"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
+ "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==",
+ "dev": true
+ }
+ }
+ },
"babel-helper-call-delegate": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
@@ -530,6 +587,18 @@
"babel-runtime": "6.25.0"
}
},
+ "babel-plugin-syntax-flow": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz",
+ "integrity": "sha1-TDqyCiryaqIM0lmVw5jE63AxDI0=",
+ "dev": true
+ },
+ "babel-plugin-syntax-jsx": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
+ "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=",
+ "dev": true
+ },
"babel-plugin-transform-es2015-arrow-functions": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
@@ -764,6 +833,56 @@
"regexpu-core": "2.0.0"
}
},
+ "babel-plugin-transform-flow-strip-types": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz",
+ "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-flow": "6.18.0",
+ "babel-runtime": "6.25.0"
+ }
+ },
+ "babel-plugin-transform-react-display-name": {
+ "version": "6.25.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz",
+ "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "6.25.0"
+ }
+ },
+ "babel-plugin-transform-react-jsx": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz",
+ "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=",
+ "dev": true,
+ "requires": {
+ "babel-helper-builder-react-jsx": "6.26.0",
+ "babel-plugin-syntax-jsx": "6.18.0",
+ "babel-runtime": "6.25.0"
+ }
+ },
+ "babel-plugin-transform-react-jsx-self": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz",
+ "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-jsx": "6.18.0",
+ "babel-runtime": "6.25.0"
+ }
+ },
+ "babel-plugin-transform-react-jsx-source": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz",
+ "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-jsx": "6.18.0",
+ "babel-runtime": "6.25.0"
+ }
+ },
"babel-plugin-transform-regenerator": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz",
@@ -826,6 +945,29 @@
"babel-plugin-transform-regenerator": "6.24.1"
}
},
+ "babel-preset-flow": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz",
+ "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-transform-flow-strip-types": "6.22.0"
+ }
+ },
+ "babel-preset-react": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-react/-/babel-preset-react-6.24.1.tgz",
+ "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=",
+ "dev": true,
+ "requires": {
+ "babel-plugin-syntax-jsx": "6.18.0",
+ "babel-plugin-transform-react-display-name": "6.25.0",
+ "babel-plugin-transform-react-jsx": "6.24.1",
+ "babel-plugin-transform-react-jsx-self": "6.22.0",
+ "babel-plugin-transform-react-jsx-source": "6.22.0",
+ "babel-preset-flow": "6.23.0"
+ }
+ },
"babel-register": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz",
@@ -1905,6 +2047,16 @@
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
"dev": true
},
+ "define-properties": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
+ "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
+ "dev": true,
+ "requires": {
+ "foreach": "2.0.5",
+ "object-keys": "1.0.11"
+ }
+ },
"defined": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
@@ -2129,6 +2281,15 @@
"integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=",
"dev": true
},
+ "encoding": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+ "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+ "dev": true,
+ "requires": {
+ "iconv-lite": "0.4.19"
+ }
+ },
"engine.io": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-1.8.3.tgz",
@@ -2259,6 +2420,38 @@
"is-arrayish": "0.2.1"
}
},
+ "es-abstract": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.9.0.tgz",
+ "integrity": "sha512-kk3IJoKo7A3pWJc0OV8yZ/VEX2oSUytfekrJiqoxBlKJMFAJVJVpGdHClCCTdv+Fn2zHfpDHHIelMFhZVfef3Q==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "1.1.1",
+ "function-bind": "1.1.1",
+ "has": "1.0.1",
+ "is-callable": "1.1.3",
+ "is-regex": "1.0.4"
+ },
+ "dependencies": {
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ }
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
+ "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
+ "dev": true,
+ "requires": {
+ "is-callable": "1.1.3",
+ "is-date-object": "1.0.1",
+ "is-symbol": "1.0.1"
+ }
+ },
"es5-ext": {
"version": "0.10.27",
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.27.tgz",
@@ -2453,6 +2646,18 @@
}
}
},
+ "eslint-plugin-react": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.4.0.tgz",
+ "integrity": "sha512-tvjU9u3VqmW2vVuYnE8Qptq+6ji4JltjOjJ9u7VAOxVYkUkyBZWRvNYKbDv5fN+L6wiA+4we9+qQahZ0m63XEA==",
+ "dev": true,
+ "requires": {
+ "doctrine": "2.0.0",
+ "has": "1.0.1",
+ "jsx-ast-utils": "2.0.1",
+ "prop-types": "15.6.0"
+ }
+ },
"eslint-scope": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
@@ -2668,6 +2873,29 @@
"integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=",
"dev": true
},
+ "fbjs": {
+ "version": "0.8.16",
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz",
+ "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=",
+ "dev": true,
+ "requires": {
+ "core-js": "1.2.7",
+ "isomorphic-fetch": "2.2.1",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1",
+ "promise": "7.3.1",
+ "setimmediate": "1.0.5",
+ "ua-parser-js": "0.7.14"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+ "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=",
+ "dev": true
+ }
+ }
+ },
"figures": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
@@ -2774,6 +3002,12 @@
"for-in": "1.0.2"
}
},
+ "foreach": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=",
+ "dev": true
+ },
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
@@ -3554,6 +3788,18 @@
"builtin-modules": "1.1.1"
}
},
+ "is-callable": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz",
+ "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=",
+ "dev": true
+ },
+ "is-date-object": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+ "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
+ "dev": true
+ },
"is-dotfile": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz",
@@ -3682,6 +3928,15 @@
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
"dev": true
},
+ "is-regex": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+ "dev": true,
+ "requires": {
+ "has": "1.0.1"
+ }
+ },
"is-resolvable": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz",
@@ -3706,6 +3961,12 @@
"html-comment-regex": "1.1.1"
}
},
+ "is-symbol": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
+ "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=",
+ "dev": true
+ },
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
@@ -3745,6 +4006,16 @@
"isarray": "1.0.0"
}
},
+ "isomorphic-fetch": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
+ "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+ "dev": true,
+ "requires": {
+ "node-fetch": "1.7.3",
+ "whatwg-fetch": "2.0.3"
+ }
+ },
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -3863,6 +4134,15 @@
}
}
},
+ "jsx-ast-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz",
+ "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=",
+ "dev": true,
+ "requires": {
+ "array-includes": "3.0.3"
+ }
+ },
"karma": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/karma/-/karma-1.7.0.tgz",
@@ -4652,6 +4932,16 @@
"lower-case": "1.1.4"
}
},
+ "node-fetch": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
+ "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+ "dev": true,
+ "requires": {
+ "encoding": "0.1.12",
+ "is-stream": "1.1.0"
+ }
+ },
"node-gyp": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz",
@@ -4866,6 +5156,12 @@
"integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=",
"dev": true
},
+ "object-keys": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz",
+ "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=",
+ "dev": true
+ },
"object.omit": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz",
@@ -5766,6 +6062,26 @@
"integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=",
"dev": true
},
+ "promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "dev": true,
+ "requires": {
+ "asap": "2.0.6"
+ }
+ },
+ "prop-types": {
+ "version": "15.6.0",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz",
+ "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=",
+ "dev": true,
+ "requires": {
+ "fbjs": "0.8.16",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1"
+ }
+ },
"prr": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz",
@@ -5912,6 +6228,30 @@
}
}
},
+ "react": {
+ "version": "16.0.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.0.0.tgz",
+ "integrity": "sha1-zn348ZQbA28Cssyp29DLHw6FXi0=",
+ "dev": true,
+ "requires": {
+ "fbjs": "0.8.16",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1",
+ "prop-types": "15.6.0"
+ }
+ },
+ "react-dom": {
+ "version": "16.0.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.0.0.tgz",
+ "integrity": "sha1-nMMHnD3NcNTG4BuEqrKn40wwP1g=",
+ "dev": true,
+ "requires": {
+ "fbjs": "0.8.16",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1",
+ "prop-types": "15.6.0"
+ }
+ },
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@@ -6853,15 +7193,6 @@
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
"dev": true
},
- "string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
- "dev": true,
- "requires": {
- "safe-buffer": "5.1.1"
- }
- },
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@@ -6895,6 +7226,15 @@
}
}
},
+ "string_decoder": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.1.1"
+ }
+ },
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
@@ -7176,6 +7516,12 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true
},
+ "ua-parser-js": {
+ "version": "0.7.14",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.14.tgz",
+ "integrity": "sha1-EQ1T+kw/MmwSEpK76skE0uAzh8o=",
+ "dev": true
+ },
"uglify-js": {
"version": "2.8.29",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
@@ -7475,6 +7821,12 @@
"source-map": "0.5.6"
}
},
+ "whatwg-fetch": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
+ "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=",
+ "dev": true
+ },
"whet.extend": {
"version": "0.9.9",
"resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz",
diff --git a/package.json b/package.json
index 36ac096..42da3a7 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"description": "Vim vixen",
"scripts": {
"start": "webpack -w --debug",
- "lint": "eslint src",
+ "lint": "eslint --ext .jsx,.js src",
"test": "karma start"
},
"repository": {
@@ -20,10 +20,13 @@
"babel-cli": "^6.24.1",
"babel-eslint": "^7.2.3",
"babel-loader": "^7.1.1",
+ "babel-plugin-transform-react-jsx": "^6.24.1",
"babel-preset-es2015": "^6.24.1",
+ "babel-preset-react": "^6.24.1",
"chai": "^4.1.1",
"css-loader": "^0.28.4",
"eslint": "^4.7.0",
+ "eslint-plugin-react": "^7.4.0",
"html-webpack-plugin": "^2.30.1",
"karma": "^1.7.0",
"karma-firefox-launcher": "^1.0.1",
@@ -34,6 +37,8 @@
"karma-webpack": "^2.0.4",
"mocha": "^3.5.0",
"node-sass": "^4.5.3",
+ "react": "^16.0.0",
+ "react-dom": "^16.0.0",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.2",
"webpack": "^3.5.3"
diff --git a/src/background/actions/settings.js b/src/background/actions/settings.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/background/actions/settings.js
diff --git a/src/background/components/background.js b/src/background/components/background.js
index 0570a5a..06b6900 100644
--- a/src/background/components/background.js
+++ b/src/background/components/background.js
@@ -22,15 +22,7 @@ export default class BackgroundComponent {
}
update() {
- let state = this.store.getState();
- this.updateSettings(state);
- }
-
- updateSettings(setting) {
- if (!setting.settings.json) {
- return;
- }
- this.settings = JSON.parse(setting.settings.json);
+ this.settings = this.store.getState();
}
onMessage(message, sender) {
@@ -58,7 +50,7 @@ export default class BackgroundComponent {
});
});
case messages.SETTINGS_QUERY:
- return Promise.resolve(this.store.getState().settings);
+ return Promise.resolve(this.store.getState().value);
case messages.CONSOLE_QUERY_COMPLETIONS:
return commands.complete(message.text, this.settings);
case messages.SETTINGS_RELOAD:
diff --git a/src/content/actions/index.js b/src/content/actions/index.js
index 0b3749d..f8db948 100644
--- a/src/content/actions/index.js
+++ b/src/content/actions/index.js
@@ -2,16 +2,13 @@ export default {
// User input
INPUT_KEY_PRESS: 'input.key,press',
INPUT_CLEAR_KEYS: 'input.clear.keys',
- INPUT_SET_KEYMAPS: 'input.set,keymaps',
+ 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',
- // Settings
- SETTING_SET_SETTINGS: 'setting.set.settings',
-
// Follow
FOLLOW_ENABLE: 'follow.enable',
FOLLOW_DISABLE: 'follow.disable',
diff --git a/src/content/actions/input.js b/src/content/actions/input.js
index cc4efac..10ff835 100644
--- a/src/content/actions/input.js
+++ b/src/content/actions/input.js
@@ -20,4 +20,11 @@ const clearKeys = () => {
};
};
-export { keyPress, clearKeys };
+const setKeymaps = (keymaps) => {
+ return {
+ type: actions.INPUT_SET_KEYMAPS,
+ keymaps,
+ };
+};
+
+export { keyPress, clearKeys, setKeymaps };
diff --git a/src/content/components/keymapper.js b/src/content/components/keymapper.js
index 8f2cead..655c3f2 100644
--- a/src/content/components/keymapper.js
+++ b/src/content/components/keymapper.js
@@ -10,14 +10,10 @@ export default class KeymapperComponent {
}
key(key, ctrl) {
- let keymaps = this.keymaps();
- if (!keymaps) {
- return;
- }
this.store.dispatch(inputActions.keyPress(key, ctrl));
let input = this.store.getState().input;
- let matched = Object.keys(keymaps).filter((keyStr) => {
+ let matched = Object.keys(input.keymaps).filter((keyStr) => {
return keyStr.startsWith(input.keys);
});
if (matched.length === 0) {
@@ -27,17 +23,9 @@ export default class KeymapperComponent {
matched.length === 1 && input.keys !== matched[0]) {
return true;
}
- let operation = keymaps[matched];
+ let operation = input.keymaps[matched];
this.store.dispatch(operationActions.exec(operation));
this.store.dispatch(inputActions.clearKeys());
return true;
}
-
- keymaps() {
- let settings = this.store.getState().setting.settings;
- if (!settings || !settings.json) {
- return null;
- }
- return JSON.parse(settings.json).keymaps;
- }
}
diff --git a/src/content/index.js b/src/content/index.js
index 63bbf77..64d86bb 100644
--- a/src/content/index.js
+++ b/src/content/index.js
@@ -1,6 +1,6 @@
import './console-frame.scss';
import * as consoleFrames from './console-frames';
-import * as settingActions from 'settings/actions/setting';
+import * as inputActions from './actions/input';
import { createStore } from 'shared/store';
import ContentInputComponent from 'content/components/content-input';
import KeymapperComponent from 'content/components/keymapper';
@@ -34,7 +34,7 @@ const reloadSettings = () => {
return browser.runtime.sendMessage({
type: messages.SETTINGS_QUERY,
}).then((settings) => {
- store.dispatch(settingActions.set(settings));
+ store.dispatch(inputActions.setKeymaps(settings.keymaps));
});
};
diff --git a/src/content/reducers/index.js b/src/content/reducers/index.js
index a62217f..c026a19 100644
--- a/src/content/reducers/index.js
+++ b/src/content/reducers/index.js
@@ -1,18 +1,15 @@
-import settingReducer from 'settings/reducers/setting';
import inputReducer from './input';
import followReducer from './follow';
// Make setting reducer instead of re-use
const defaultState = {
input: inputReducer(undefined, {}),
- setting: settingReducer(undefined, {}),
follow: followReducer(undefined, {}),
};
export default function reducer(state = defaultState, action = {}) {
return Object.assign({}, state, {
input: inputReducer(state.input, action),
- setting: settingReducer(state.setting, action),
follow: followReducer(state.follow, action),
});
}
diff --git a/src/content/reducers/input.js b/src/content/reducers/input.js
index 802020f..c79b206 100644
--- a/src/content/reducers/input.js
+++ b/src/content/reducers/input.js
@@ -2,6 +2,7 @@ import actions from 'content/actions';
const defaultState = {
keys: '',
+ keymaps: {},
};
export default function reducer(state = defaultState, action = {}) {
@@ -14,6 +15,10 @@ export default function reducer(state = defaultState, action = {}) {
return Object.assign({}, state, {
keys: '',
});
+ case actions.INPUT_SET_KEYMAPS:
+ return Object.assign({}, state, {
+ keymaps: action.keymaps,
+ });
default:
return state;
}
diff --git a/src/settings/actions/setting.js b/src/settings/actions/setting.js
index 697bcf0..c1b27c8 100644
--- a/src/settings/actions/setting.js
+++ b/src/settings/actions/setting.js
@@ -3,9 +3,9 @@ import messages from 'shared/messages';
import DefaultSettings from 'shared/default-settings';
const load = () => {
- return browser.storage.local.get('settings').then((value) => {
- if (value.settings) {
- return set(value.settings);
+ return browser.storage.local.get('settings').then(({ settings }) => {
+ if (settings) {
+ return set(settings);
}
return set(DefaultSettings);
}, console.error);
@@ -13,10 +13,12 @@ const load = () => {
const save = (settings) => {
return browser.storage.local.set({
- settings
+ settings,
}).then(() => {
return browser.runtime.sendMessage({
type: messages.SETTINGS_RELOAD
+ }).then(() => {
+ return set(settings);
});
});
};
@@ -24,8 +26,10 @@ const save = (settings) => {
const set = (settings) => {
return {
type: actions.SETTING_SET_SETTINGS,
- settings,
+ source: settings.source,
+ json: settings.json,
+ value: JSON.parse(settings.json),
};
};
-export { load, save, set };
+export { load, save };
diff --git a/src/settings/components/index.jsx b/src/settings/components/index.jsx
new file mode 100644
index 0000000..4418942
--- /dev/null
+++ b/src/settings/components/index.jsx
@@ -0,0 +1,91 @@
+import './site.scss';
+import React from 'react';
+import PropTypes from 'prop-types';
+import * as settingActions from 'settings/actions/setting';
+import * as validator from 'shared/validators/setting';
+
+class SettingsComponent extends React.Component {
+ constructor(props, context) {
+ super(props, context);
+
+ this.state = {
+ settings: {
+ json: '',
+ }
+ };
+ this.context.store.subscribe(this.stateChanged.bind(this));
+ }
+
+ componentDidMount() {
+ this.context.store.dispatch(settingActions.load());
+ }
+
+ stateChanged() {
+ let settings = this.context.store.getState();
+ this.setState({
+ settings: {
+ source: settings.source,
+ json: settings.json,
+ }
+ });
+ }
+
+ render() {
+ return (
+ <div>
+ <h1>Configure Vim-Vixen</h1>
+ <form className='vimvixen-settings-form'>
+
+ <p>Load settings from:</p>
+ <input type='radio' id='setting-source-json'
+ name='source'
+ value='json'
+ onChange={this.bindAndSave.bind(this)}
+ checked={this.state.settings.source === 'json'} />
+ <label htmlFor='settings-source-json'>JSON</label>
+
+ <textarea name='json' spellCheck='false'
+ onInput={this.validate.bind(this)}
+ onChange={this.bindValue.bind(this)}
+ onBlur={this.bindAndSave.bind(this)}
+ value={this.state.settings.json} />
+ </form>
+ </div>
+ );
+ }
+
+ validate(e) {
+ try {
+ let settings = JSON.parse(e.target.value);
+ validator.validate(settings);
+ e.target.setCustomValidity('');
+ } catch (err) {
+ e.target.setCustomValidity(err.message);
+ }
+ }
+
+ bindValue(e) {
+ let nextSettings = Object.assign({}, this.state.settings);
+ nextSettings[e.target.name] = e.target.value;
+
+ this.setState({ settings: nextSettings });
+ }
+
+ bindAndSave(e) {
+ this.bindValue(e);
+
+ try {
+ let json = this.state.settings.json;
+ validator.validate(JSON.parse(json));
+ this.context.store.dispatch(settingActions.save(this.state.settings));
+ } catch (err) {
+ // error already shown
+ }
+ }
+}
+
+SettingsComponent.contextTypes = {
+ store: PropTypes.any,
+};
+
+export default SettingsComponent;
diff --git a/src/settings/components/setting.js b/src/settings/components/setting.js
deleted file mode 100644
index 14482a3..0000000
--- a/src/settings/components/setting.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import * as settingActions from 'settings/actions/setting';
-import { validate } from 'shared/validators/setting';
-
-export default class SettingComponent {
- constructor(wrapper, store) {
- this.wrapper = wrapper;
- this.store = store;
-
- let doc = wrapper.ownerDocument;
- let form = doc.getElementById('vimvixen-settings-form');
- form.addEventListener('submit', this.onSubmit.bind(this));
-
- let plainJson = form.elements['plain-json'];
- plainJson.addEventListener('input', this.onPlainJsonChanged.bind(this));
-
- store.dispatch(settingActions.load());
- }
-
- onSubmit(e) {
- let settings = {
- json: e.target.elements['plain-json'].value,
- };
- this.store.dispatch(settingActions.save(settings));
- e.preventDefault();
- }
-
- onPlainJsonChanged(e) {
- try {
- let settings = JSON.parse(e.target.value);
- validate(settings);
- e.target.setCustomValidity('');
- } catch (err) {
- e.target.setCustomValidity(err.message);
- }
- }
-
- update() {
- let { settings } = this.store.getState();
-
- let doc = this.wrapper.ownerDocument;
- let form = doc.getElementById('vimvixen-settings-form');
- let plainJsonInput = form.elements['plain-json'];
- plainJsonInput.value = settings.json;
- }
-}
diff --git a/src/settings/site.scss b/src/settings/components/site.scss
index 5707c8a..fae9c39 100644
--- a/src/settings/site.scss
+++ b/src/settings/components/site.scss
@@ -1,5 +1,5 @@
.vimvixen-settings-form {
- textarea[name=plain-json] {
+ textarea[name=json] {
font-family: monospace;
width: 100%;
min-height: 64ex;
diff --git a/src/settings/index.html b/src/settings/index.html
index 99d6c6b..6fe00df 100644
--- a/src/settings/index.html
+++ b/src/settings/index.html
@@ -4,15 +4,7 @@
<meta charset='utf-8'>
</head>
<body>
- <h1>Configure</h1>
-
- <h2>Home page</h2>
- <form id='vimvixen-settings-form' class='vimvixen-settings-form'>
- <label for='load-from-json'>Load from JSON:</label>
- <textarea name='plain-json' spellcheck='false'></textarea>
-
- <button type='submit'>Save</button>
- </form>
+ <div id='vimvixen-settings'></div>
<script src='settings.js'></script>
</body>
</html>
diff --git a/src/settings/index.js b/src/settings/index.js
deleted file mode 100644
index c8d6cc4..0000000
--- a/src/settings/index.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import './site.scss';
-import SettingComponent from 'settings/components/setting';
-import settingReducer from 'settings/reducers/setting';
-import { createStore } from 'shared/store';
-
-const store = createStore(settingReducer);
-let settingComponent = null;
-
-store.subscribe(() => {
- settingComponent.update();
-});
-
-document.addEventListener('DOMContentLoaded', () => {
- settingComponent = new SettingComponent(document.body, store);
-});
diff --git a/src/settings/index.jsx b/src/settings/index.jsx
new file mode 100644
index 0000000..7516fb7
--- /dev/null
+++ b/src/settings/index.jsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import SettingsComponent from './components';
+import reducer from 'settings/reducers/setting';
+import Provider from 'shared/store/provider';
+import { createStore } from 'shared/store';
+
+const store = createStore(reducer);
+
+document.addEventListener('DOMContentLoaded', () => {
+ let wrapper = document.getElementById('vimvixen-settings');
+ ReactDOM.render(
+ <Provider store={store}>
+ <SettingsComponent />
+ </Provider>,
+ wrapper
+ );
+});
diff --git a/src/settings/reducers/setting.js b/src/settings/reducers/setting.js
index f7d9242..a61c09f 100644
--- a/src/settings/reducers/setting.js
+++ b/src/settings/reducers/setting.js
@@ -1,15 +1,19 @@
import actions from 'settings/actions';
const defaultState = {
- settings: {}
+ source: '',
+ json: '',
+ value: {}
};
export default function reducer(state = defaultState, action = {}) {
switch (action.type) {
case actions.SETTING_SET_SETTINGS:
- return Object.assign({}, state, {
- settings: action.settings,
- });
+ return {
+ source: action.source,
+ json: action.json,
+ value: action.value,
+ };
default:
return state;
}
diff --git a/src/shared/default-settings.js b/src/shared/default-settings.js
index 24ac536..f287b7a 100644
--- a/src/shared/default-settings.js
+++ b/src/shared/default-settings.js
@@ -1,4 +1,5 @@
export default {
+ source: 'json',
json: `{
"keymaps": {
"0": { "type": "scroll.home" },
diff --git a/src/shared/store/provider.jsx b/src/shared/store/provider.jsx
new file mode 100644
index 0000000..743f656
--- /dev/null
+++ b/src/shared/store/provider.jsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+class Provider extends React.PureComponent {
+ getChildContext() {
+ return { store: this.props.store };
+ }
+
+ render() {
+ return React.Children.only(this.props.children);
+ }
+}
+
+Provider.childContextTypes = {
+ store: PropTypes.any,
+};
+
+export default Provider;
diff --git a/test/settings/reducers/setting.test.js b/test/settings/reducers/setting.test.js
index 0e84247..3468d4b 100644
--- a/test/settings/reducers/setting.test.js
+++ b/test/settings/reducers/setting.test.js
@@ -5,18 +5,18 @@ import settingReducer from 'settings/reducers/setting';
describe("setting reducer", () => {
it('return the initial state', () => {
let state = settingReducer(undefined, {});
- expect(state).to.have.deep.property('settings', {});
+ expect(state).to.have.deep.property('json', '');
+ expect(state).to.have.deep.property('value', {});
});
it('return next state for SETTING_SET_SETTINGS', () => {
let action = {
type: actions.SETTING_SET_SETTINGS,
- settings: { value1: 'hello', value2: 'world' },
+ json: '{ "key": "value" }',
+ value: { key: 123 },
};
let state = settingReducer(undefined, action);
- expect(state).to.have.deep.property('settings', {
- value1: 'hello',
- value2: 'world',
- });
+ expect(state).to.have.deep.property('json', '{ "key": "value" }');
+ expect(state).to.have.deep.property('value', { key: 123 });
});
});
diff --git a/webpack.config.js b/webpack.config.js
index fff49d1..977ea75 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -20,11 +20,11 @@ module.exports = {
module: {
loaders: [
{
- test: /\.js$/,
+ test: [ /\.js$/, /\.jsx$/ ],
exclude: /node_modules/,
loader: 'babel-loader',
query: {
- presets: [ 'es2015' ]
+ presets: ['es2015', 'react']
}
},
{
@@ -39,7 +39,7 @@ module.exports = {
},
resolve: {
- extensions: [ '.js' ],
+ extensions: [ '.js', '.jsx' ],
modules: [path.join(__dirname, 'src'), 'node_modules']
},