diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..e8e445c83 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,121 @@ +{ + "extends": ["eslint:recommended", "prettier"], + "parser": "babel-eslint", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module", + "ecmaFeatures": { + "arrowFunctions": true, + "blockBindings": true, + "classes": true, + "defaultParams": true, + "destructuring": true, + "forOf": true, + "generators": true, + "modules": true, + "templateStrings": true, + "jsx": true + } + }, + "env": { + "browser": true, + "es6": true, + "jasmine": true, + "jest": true, + "node": true + }, + "globals": { + "jest": true + }, + "plugins": [ + "react", + "import" + ], + "rules": { + "accessor-pairs": ["error"], + "block-scoped-var": ["error"], + "consistent-return": ["error"], + "curly": ["error", "all"], + "default-case": ["error"], + "dot-location": ["off"], + "dot-notation": ["error"], + "eqeqeq": ["error"], + "guard-for-in": ["off"], + "import/named": ["off"], + "import/no-duplicates": ["error"], + "import/no-named-as-default": ["error"], + "new-cap": ["error"], + "no-alert": [1], + "no-caller": ["error"], + "no-case-declarations": ["error"], + "no-console": ["error"], + "no-div-regex": ["error"], + "no-dupe-keys": ["error"], + "no-else-return": ["error"], + "no-empty-pattern": ["error"], + "no-eq-null": ["error"], + "no-eval": ["error"], + "no-extend-native": ["error"], + "no-extra-bind": ["error"], + "no-extra-boolean-cast": ["error"], + "no-inline-comments": ["error"], + "no-implicit-coercion": ["error"], + "no-implied-eval": ["error"], + "no-inner-declarations": ["off"], + "no-invalid-this": ["error"], + "no-iterator": ["error"], + "no-labels": ["error"], + "no-lone-blocks": ["error"], + "no-loop-func": ["error"], + "no-multi-str": ["error"], + "no-native-reassign": ["error"], + "no-new": ["error"], + "no-new-func": ["error"], + "no-new-wrappers": ["error"], + "no-param-reassign": ["error"], + "no-process-env": ["warn"], + "no-proto": ["error"], + "no-redeclare": ["error"], + "no-return-assign": ["error"], + "no-script-url": ["error"], + "no-self-compare": ["error"], + "no-sequences": ["error"], + "no-shadow": ["off"], + "no-throw-literal": ["error"], + "no-undefined": ["error"], + "no-unused-expressions": ["error"], + "no-use-before-define": ["error", "nofunc"], + "no-useless-call": ["error"], + "no-useless-concat": ["error"], + "no-with": ["error"], + "prefer-const": ["error"], + "radix": ["error"], + "react/jsx-no-duplicate-props": ["error"], + "react/jsx-no-undef": ["error"], + "react/jsx-uses-react": ["error"], + "react/jsx-uses-vars": ["error"], + "react/no-did-update-set-state": ["error"], + "react/no-direct-mutation-state": ["error"], + "react/no-is-mounted": ["error"], + "react/no-unknown-property": ["error"], + "react/prefer-es6-class": ["error", "always"], + "react/prop-types": "error", + "valid-jsdoc": ["error"], + "yoda": ["error"], + "spaced-comment": ["error", "always", { + "block": { + exceptions: ["*"] + } + }], + "no-unused-vars": ["error", { + "args": "after-used", + "argsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^e$" + }], + "no-magic-numbers": ["error", { + "ignoreArrayIndexes": true, + "ignore": [-1, 0, 1, 2, 100, 10, 16, 0.5, 25] + }], + "no-underscore-dangle": ["off"] + } +} diff --git a/package-lock.json b/package-lock.json index 75dc36698..ff138a400 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,149 @@ "right-now": "1.0.0" } }, + "@babel/code-frame": { + "version": "7.0.0-beta.32", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.32.tgz", + "integrity": "sha512-EVq4T1a2GviKiQ75OfxNrGPPhJyXzg9jjORuuwhloZbFdrhT4FHa73sv9OFWBwX7rl2b6bxBVmfxrBQYWYz9tA==", + "dev": true, + "requires": { + "chalk": "2.3.0", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.0.0-beta.32", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.32.tgz", + "integrity": "sha512-ysfIt7p72xm5fjSJsv7fMVN/j+EwIdqu8/MJjt6TqB4wM2r6rFRi0ujBTWDkLGQkRB/P5uDV8qcFCHAHnNzmsg==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "7.0.0-beta.32", + "@babel/template": "7.0.0-beta.32", + "@babel/types": "7.0.0-beta.32" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0-beta.32", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.32.tgz", + "integrity": "sha512-bm7lIlizycJQY5SJ3HXWJV4XjSrOt1onzrDcOxUo9FEnKRZDEr/zfi5ar2s5tvvZvve/jGHwZKVKekRw2cjPCQ==", + "dev": true, + "requires": { + "@babel/types": "7.0.0-beta.32" + } + }, + "@babel/template": { + "version": "7.0.0-beta.32", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.32.tgz", + "integrity": "sha512-DB9sLgX2mfE29vjAkxHlzLyWr31EO9HaYoAM/UsPSsL70Eudl0i25URwIfQT6S6ckeVFnFP1t6PhERVeV4EAHA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0-beta.32", + "@babel/types": "7.0.0-beta.32", + "babylon": "7.0.0-beta.32", + "lodash": "4.17.4" + }, + "dependencies": { + "babylon": { + "version": "7.0.0-beta.32", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.32.tgz", + "integrity": "sha512-PvAmyP2IJEBVAuE5yVzrTSWCCN9VMa1eGns8w3w6FYD/ivHSUmS7n+F40Fmjn+0nCQSUFR96wP0CqQ4jxTnF4Q==", + "dev": true + } + } + }, + "@babel/traverse": { + "version": "7.0.0-beta.32", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.32.tgz", + "integrity": "sha512-dGe2CLduCIZ/iDkbmnqspQguRy5ARvI+zC8TiwFnsJ2YYO2TWK7x2aEwrbkSmi0iPlBP+Syiag7Idc1qNQq74g==", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0-beta.32", + "@babel/helper-function-name": "7.0.0-beta.32", + "@babel/types": "7.0.0-beta.32", + "babylon": "7.0.0-beta.32", + "debug": "3.1.0", + "globals": "10.3.0", + "invariant": "2.2.2", + "lodash": "4.17.4" + }, + "dependencies": { + "babylon": { + "version": "7.0.0-beta.32", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.32.tgz", + "integrity": "sha512-PvAmyP2IJEBVAuE5yVzrTSWCCN9VMa1eGns8w3w6FYD/ivHSUmS7n+F40Fmjn+0nCQSUFR96wP0CqQ4jxTnF4Q==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "globals": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-10.3.0.tgz", + "integrity": "sha512-1g6qO5vMbiPHbRTDtR9JVjRkAhkgH4nSANYGyx1eOfqgxcMnYMMD+7MjmjfzXjwFpVUE/7/NzF+jQxYE7P4r7A==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.0.0-beta.32", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.32.tgz", + "integrity": "sha512-w8+wzVcYCMb9OfaBfay2Vg5hyj7UfBX6qQtA+kB0qsW1h1NH/7xHMwvTZNqkuFBwjz5wxGS2QmaIcC3HH+UoxA==", + "dev": true, + "requires": { + "esutils": "2.0.2", + "lodash": "4.17.4", + "to-fast-properties": "2.0.0" + }, + "dependencies": { + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, "@plotly/d3-sankey": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@plotly/d3-sankey/-/d3-sankey-0.5.0.tgz", @@ -101,6 +244,23 @@ } } }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "3.3.0" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, "add-line-numbers": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/add-line-numbers/-/add-line-numbers-1.0.1.tgz", @@ -128,6 +288,12 @@ "json-stable-stringify": "1.0.1" } }, + "ajv-keywords": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", + "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", + "dev": true + }, "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", @@ -353,6 +519,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-map": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", @@ -373,6 +549,21 @@ "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=", "dev": true }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, "array-unique": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", @@ -623,6 +814,26 @@ } } }, + "babel-eslint": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.0.2.tgz", + "integrity": "sha512-yyl5U088oE+419+BNLJDKVWkUokuPLQeQt9ZTy9uM9kAzbtQgyYL3JkG425B8jxXA7MwTxnDAtRLMKJNH36qjA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0-beta.32", + "@babel/traverse": "7.0.0-beta.32", + "@babel/types": "7.0.0-beta.32", + "babylon": "7.0.0-beta.32" + }, + "dependencies": { + "babylon": { + "version": "7.0.0-beta.32", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.32.tgz", + "integrity": "sha512-PvAmyP2IJEBVAuE5yVzrTSWCCN9VMa1eGns8w3w6FYD/ivHSUmS7n+F40Fmjn+0nCQSUFR96wP0CqQ4jxTnF4Q==", + "dev": true + } + } + }, "babel-generator": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", @@ -2335,6 +2546,23 @@ } } }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "0.2.0" + }, + "dependencies": { + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + } + } + }, "callsites": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", @@ -2518,6 +2746,12 @@ "safe-buffer": "5.1.1" } }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "dev": true + }, "circumcenter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/circumcenter/-/circumcenter-1.0.0.tgz", @@ -2861,6 +3095,12 @@ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "dev": true }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "content-type-parser": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/content-type-parser/-/content-type-parser-1.0.2.tgz", @@ -3267,6 +3507,21 @@ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "dev": true, + "requires": { + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" + } + }, "delaunay-triangulate": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/delaunay-triangulate/-/delaunay-triangulate-1.1.6.tgz", @@ -3413,6 +3668,30 @@ "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=", "dev": true }, + "doctrine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", + "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", + "dev": true, + "requires": { + "esutils": "2.0.2", + "isarray": "1.0.0" + }, + "dependencies": { + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + } + } + }, "dom-serializer": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", @@ -3753,53 +4032,460 @@ "es6-symbol": "3.1.1" } }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.35", + "es6-symbol": "3.1.1" + } + }, + "es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "dev": true, + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.35" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz", + "integrity": "sha1-8CQBb1qI4Eb9EgBQVek5gC5sXyM=", + "requires": { + "esprima": "1.1.1", + "estraverse": "1.5.1", + "esutils": "1.0.0", + "source-map": "0.1.43" + } + }, + "eslint": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.11.0.tgz", + "integrity": "sha512-UWbhQpaKlm8h5x/VLwm0S1kheMrDj8jPwhnBMjr/Dlo3qqT7MvcN/UfKAR3E1N4lr4YNtOvS4m3hwsrVc/ky7g==", + "dev": true, + "requires": { + "ajv": "5.3.0", + "babel-code-frame": "6.26.0", + "chalk": "2.3.0", + "concat-stream": "1.6.0", + "cross-spawn": "5.1.0", + "debug": "3.1.0", + "doctrine": "2.0.0", + "eslint-scope": "3.7.1", + "espree": "3.5.2", + "esquery": "1.0.0", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.2", + "globals": "9.18.0", + "ignore": "3.3.7", + "imurmurhash": "0.1.4", + "inquirer": "3.3.0", + "is-resolvable": "1.0.0", + "js-yaml": "3.10.0", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.4", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "7.0.0", + "progress": "2.0.0", + "require-uncached": "1.0.3", + "semver": "5.4.1", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", + "table": "4.0.2", + "text-table": "0.2.0" + }, + "dependencies": { + "ajv": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz", + "integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" + } + }, + "ansi-escapes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz", + "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "1.0.5" + } + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "3.0.0", + "chalk": "2.3.0", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "2.0.5", + "figures": "2.0.0", + "lodash": "4.17.4", + "mute-stream": "0.0.7", + "run-async": "2.3.0", + "rx-lite": "4.0.8", + "rx-lite-aggregates": "4.0.8", + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "through": "2.3.8" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "1.1.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "2.0.1", + "signal-exit": "3.0.2" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "2.1.0" + } + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, + "eslint-config-prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-2.7.0.tgz", + "integrity": "sha1-e7/vZq14MneDb06lVuaLm8ydpNA=", + "dev": true, + "requires": { + "get-stdin": "5.0.1" + }, + "dependencies": { + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "dev": true + } + } + }, + "eslint-import-resolver-node": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.1.tgz", + "integrity": "sha512-yUtXS15gIcij68NmXmP9Ni77AQuCN0itXbCc/jWd8C6/yKZaSNXicpC8cgvjnxVdmfsosIXrjpzFq7GcDryb6A==", + "dev": true, + "requires": { + "debug": "2.6.9", + "resolve": "1.4.0" + } + }, + "eslint-module-utils": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz", + "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "pkg-dir": "1.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.8.0.tgz", + "integrity": "sha512-Rf7dfKJxZ16QuTgVv1OYNxkZcsu/hULFnC+e+w0Gzi6jMC3guQoWQgxYxc54IDRinlb6/0v5z/PxxIKmVctN+g==", + "dev": true, + "requires": { + "builtin-modules": "1.1.1", + "contains-path": "0.1.0", + "debug": "2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "0.3.1", + "eslint-module-utils": "2.1.1", + "has": "1.0.1", + "lodash.cond": "4.5.2", + "minimatch": "3.0.4", + "read-pkg-up": "2.0.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "2.0.2", + "isarray": "1.0.0" + } + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" + } + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "2.3.0" + } + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "2.0.0", + "normalize-package-data": "2.4.0", + "path-type": "2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "2.1.0", + "read-pkg": "2.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + } + } + }, + "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": { - "d": "1.0.0", - "es5-ext": "0.10.35", - "es6-symbol": "3.1.1" + "doctrine": "2.0.0", + "has": "1.0.1", + "jsx-ast-utils": "2.0.1", + "prop-types": "15.6.0" } }, - "es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=" - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.35" + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } } }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz", - "integrity": "sha1-8CQBb1qI4Eb9EgBQVek5gC5sXyM=", + "espree": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz", + "integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==", + "dev": true, "requires": { - "esprima": "1.1.1", - "estraverse": "1.5.1", - "esutils": "1.0.0", - "source-map": "0.1.43" + "acorn": "5.2.1", + "acorn-jsx": "3.0.1" + }, + "dependencies": { + "acorn": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz", + "integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w==", + "dev": true + } } }, "esprima": { @@ -3815,6 +4501,41 @@ "core-js": "2.5.1" } }, + "esquery": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "dev": true, + "requires": { + "estraverse": "4.2.0" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "dev": true, + "requires": { + "estraverse": "4.2.0", + "object-assign": "4.1.1" + }, + "dependencies": { + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + } + } + }, "estraverse": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", @@ -3955,6 +4676,17 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" }, + "external-editor": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.0.5.tgz", + "integrity": "sha512-Msjo64WT5W+NhOpQXh0nOHm+n0RfU1QUwDnKYvJ8dEJ8zlwLrqXNTv5mSUTJpepf41PDJGyhueTw2vNZW+Fr/w==", + "dev": true, + "requires": { + "iconv-lite": "0.4.19", + "jschardet": "1.6.0", + "tmp": "0.0.33" + } + }, "extglob": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", @@ -3995,6 +4727,12 @@ "resolved": "https://registry.npmjs.org/fast-isnumeric/-/fast-isnumeric-1.1.1.tgz", "integrity": "sha1-V7gcB6PAnLnsO++cFhgYmS2JNkM=" }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -4045,6 +4783,16 @@ "object-assign": "4.1.1" } }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "dev": true, + "requires": { + "flat-cache": "1.3.0", + "object-assign": "4.1.1" + } + }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", @@ -4102,6 +4850,18 @@ "commander": "2.1.0" } }, + "flat-cache": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "dev": true, + "requires": { + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" + } + }, "font-atlas-sdf": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/font-atlas-sdf/-/font-atlas-sdf-1.3.3.tgz", @@ -5668,6 +6428,20 @@ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", "dev": true }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, "globule": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.0.tgz", @@ -6173,6 +6947,12 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" }, + "ignore": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", + "integrity": "sha512-YGG3ejvBNHRqu0559EOxxNFihD0AjpvHlC/pdGKd3X3ofe+CoJkYazwNJYTNebqpPKN+VVQbh4ZFn1DivMNuHA==", + "dev": true + }, "ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", @@ -6616,6 +7396,21 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true + }, + "is-path-in-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "dev": true, + "requires": { + "is-path-inside": "1.0.0" + } + }, "is-path-inside": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", @@ -6685,6 +7480,15 @@ "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", "dev": true }, + "is-resolvable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", + "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", + "dev": true, + "requires": { + "tryit": "1.0.3" + } + }, "is-retry-allowed": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", @@ -7865,6 +8669,12 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "optional": true }, + "jschardet": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-1.6.0.tgz", + "integrity": "sha512-xYuhvQ7I9PDJIGBWev9xm0+SMSed3ZDBAmvVjbFR1ZRLAF+vlXcQu6cRI9uAlj81rzikElRVteehwV7DuX2ZmQ==", + "dev": true + }, "jsdom": { "version": "9.12.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-9.12.0.tgz", @@ -7968,6 +8778,12 @@ "jsonify": "0.0.0" } }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -8010,6 +8826,15 @@ "verror": "1.10.0" } }, + "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" + } + }, "kdbush": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-1.0.1.tgz", @@ -8482,6 +9307,12 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, + "lodash.cond": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", + "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=", + "dev": true + }, "lodash.flattendeep": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", @@ -11371,6 +12202,15 @@ "pinkie": "2.0.4" } }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "1.1.2" + } + }, "planar-dual": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/planar-dual/-/planar-dual-1.0.2.tgz", @@ -11457,6 +12297,12 @@ "integrity": "sha1-24XGgU9eXlo7Se/CjWBP7GKXUVY=", "dev": true }, + "pluralize": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", + "dev": true + }, "pngjs": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-2.3.1.tgz", @@ -11637,6 +12483,12 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, + "progress": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "dev": true + }, "promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", @@ -12290,6 +13142,16 @@ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", "dev": true }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "dev": true, + "requires": { + "caller-path": "0.1.0", + "resolve-from": "1.0.1" + } + }, "resolve": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", @@ -12298,6 +13160,12 @@ "path-parse": "1.0.5" } }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", + "dev": true + }, "resolve-protobuf-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.0.0.tgz", @@ -12498,6 +13366,15 @@ "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", "dev": true }, + "rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74=", + "dev": true, + "requires": { + "rx-lite": "3.1.2" + } + }, "rxjs": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.0.tgz", @@ -13592,6 +14469,82 @@ } } }, + "table": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "dev": true, + "requires": { + "ajv": "5.2.3", + "ajv-keywords": "2.1.1", + "chalk": "2.3.0", + "lodash": "4.17.4", + "slice-ansi": "1.0.0", + "string-width": "2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", + "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, "tape": { "version": "4.8.0", "resolved": "https://registry.npmjs.org/tape/-/tape-4.8.0.tgz", @@ -13750,6 +14703,12 @@ "vectorize-text": "3.0.2" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, "throat": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", @@ -13795,6 +14754,15 @@ "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz", "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g=" }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "1.0.2" + } + }, "tmpl": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", @@ -13898,6 +14866,12 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "tryit": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", + "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", + "dev": true + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -14715,6 +15689,15 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "dev": true, + "requires": { + "mkdirp": "0.5.1" + } + }, "write-file-atomic": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", diff --git a/package.json b/package.json index 4f7fab926..cc8d738ac 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "lint": "prettier --write \"{src}/**/*.{js,jsx}\"", "test": "jest", "watch": "nodemon --exec \"npm run make:lib\" -w src", - "watch-test": "jest --watch" + "watch-test": "jest --watch", + "eslint:check": "eslint --print-config .eslintrc | eslint-config-prettier-check" }, "keywords": [ "graphing", @@ -32,6 +33,7 @@ "devDependencies": { "autoprefixer": "^7.1.2", "babel-cli": "^6.26.0", + "babel-eslint": "^8.0.2", "babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-object-rest-spread": "^6.26.0", @@ -48,6 +50,10 @@ "envify": "^4.1.0", "enzyme": "^3.1.0", "enzyme-adapter-react-15": "^1.0.4", + "eslint": "^4.11.0", + "eslint-config-prettier": "^2.7.0", + "eslint-plugin-import": "^2.8.0", + "eslint-plugin-react": "^7.4.0", "event-emitter": "^0.3.5", "gl": "^4.0.4", "hat": "0.0.3", diff --git a/src/DefaultEditor.js b/src/DefaultEditor.js index df421feab..629b654e4 100644 --- a/src/DefaultEditor.js +++ b/src/DefaultEditor.js @@ -1,7 +1,9 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import { - SubPanel, + AxesSelector, + AxesRange, + CanvasSize, ColorPicker, DataSelector, Dropdown, @@ -13,41 +15,19 @@ import { PanelMenuWrapper, Radio, Section, + MenuPanel, + SymbolSelector, TraceAccordion, + TraceMarkerSection, TraceSelector, } from './components'; import {DEFAULT_FONTS} from './constants'; -import {localize, connectLayoutToPlot} from './lib'; +import {localize, connectAxesToLayout, connectLayoutToPlot} from './lib'; const LayoutPanel = connectLayoutToPlot(Panel); +const AxesFold = connectAxesToLayout(Fold); class DefaultEditor extends Component { - constructor(props, context) { - super(props, context); - - const capitalize = s => s.charAt(0).toUpperCase() + s.substring(1); - - // Filter out Polar "area" type (it is fairly broken and we want to present - // scatter with fill as an "area" chart type for convenience. - const traceTypes = Object.keys(context.plotSchema.traces).filter( - t => t !== 'area' - ); - - const labels = traceTypes.map(capitalize); - this.traceOptions = traceTypes.map((t, i) => ({ - label: labels[i], - value: t, - })); - - const i = this.traceOptions.findIndex(opt => opt.value === 'scatter'); - this.traceOptions.splice( - i + 1, - 0, - {label: 'Line', value: 'line'}, - {label: 'Area', value: 'area'} - ); - } - render() { const _ = this.props.localize; @@ -59,7 +39,6 @@ class DefaultEditor extends Component { label="Plot Type" attr="type" clearable={false} - options={this.traceOptions} show /> @@ -111,6 +90,18 @@ class DefaultEditor extends Component { +
+ +
+
-
- + - - - + + - - -
+ + + +
- - - + - + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+
+ + {/* + + + + + + + + + + + + + + */} +
+
@@ -223,7 +297,7 @@ class DefaultEditor extends Component { />
- +
{_( @@ -248,7 +322,7 @@ class DefaultEditor extends Component { ]} />
-
+ + + {this.props.label} + + + {isOpen ? ( + {this.props.children} + ) : null} + + ); + } +} + +MenuPanel.propTypes = { + children: PropTypes.node, + iconClass: PropTypes.string, + show: PropTypes.bool, + ownline: PropTypes.bool, + question: PropTypes.bool, + label: PropTypes.string, +}; diff --git a/src/components/containers/ModalBox.js b/src/components/containers/ModalBox.js new file mode 100644 index 000000000..c1ec315d6 --- /dev/null +++ b/src/components/containers/ModalBox.js @@ -0,0 +1,24 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; + +export default class ModalBox extends Component { + render() { + let style; + if (this.props.backgroundColor) { + style = {backgroundColor: this.props.backgroundColor}; + } + + return ( +
+
+
{this.props.children}
+
+ ); + } +} + +ModalBox.propTypes = { + backgroundColor: PropTypes.string, + children: PropTypes.node, + onClose: PropTypes.func, +}; diff --git a/src/components/containers/Section.js b/src/components/containers/Section.js index 5b681598a..264f390ad 100644 --- a/src/components/containers/Section.js +++ b/src/components/containers/Section.js @@ -1,7 +1,6 @@ -import SubPanel from './SubPanel'; +import MenuPanel from './MenuPanel'; import React, {Component, cloneElement} from 'react'; import PropTypes from 'prop-types'; -import {icon} from '../../lib'; import unpackPlotProps from '../../lib/unpackPlotProps'; function childIsVisible(child) { @@ -13,7 +12,7 @@ class Section extends Component { super(props, context); this.children = null; - this.subPanel = null; + this.menuPanel = null; this.processAndSetChildren(context); } @@ -29,45 +28,50 @@ class Section extends Component { } const attrChildren = []; - let subPanel = null; + let menuPanel = null; for (let i = 0; i < children.length; i++) { - let child = children[i]; + const child = children[i]; if (!child) { continue; } - if (child.type === SubPanel) { - // Process the first subPanel. Ignore the rest. - if (subPanel) { + if (child.type === MenuPanel) { + // Process the first menuPanel. Ignore the rest. + if (menuPanel) { continue; } - subPanel = child; + menuPanel = child; continue; } - let isAttr = !!child.props.attr; - let plotProps = isAttr - ? unpackPlotProps(child.props, context, child.constructor) - : {isVisible: true}; - let childProps = Object.assign({plotProps}, child.props); + const isAttr = Boolean(child.props.attr); + let plotProps; + if (child.plotProps) { + plotProps = child.plotProps; + } else if (isAttr) { + plotProps = unpackPlotProps(child.props, context, child.type); + } else { + plotProps = {isVisible: true}; + } + const childProps = Object.assign({plotProps}, child.props); childProps.key = i; attrChildren.push(cloneElement(child, childProps)); } this.children = attrChildren.length ? attrChildren : null; - this.subPanel = subPanel; + this.menuPanel = menuPanel; } render() { const hasVisibleChildren = (this.children && this.children.some(childIsVisible)) || - Boolean(this.subPanel); + Boolean(this.menuPanel); return hasVisibleChildren ? (
{this.props.name} - {this.subPanel} + {this.menuPanel}
{this.children}
@@ -75,8 +79,14 @@ class Section extends Component { } } +Section.propTypes = { + children: PropTypes.node, + name: PropTypes.string, +}; + Section.contextTypes = { container: PropTypes.object, + defaultContainer: PropTypes.object, fullContainer: PropTypes.object, getValObject: PropTypes.func, updateContainer: PropTypes.func, diff --git a/src/components/containers/SubPanel.js b/src/components/containers/SubPanel.js deleted file mode 100644 index e20e9a5fc..000000000 --- a/src/components/containers/SubPanel.js +++ /dev/null @@ -1,42 +0,0 @@ -import React, {Component} from 'react'; -import PropTypes from 'prop-types'; - -export default class SubPanel extends Component { - constructor() { - super(); - this.state = {isVisible: false}; - - this.toggleVisibility = this.toggleVisibility.bind(this); - } - - toggleVisibility() { - this.setState({isVisible: !this.state.isVisible}); - } - - render() { - const toggleClass = `subpanel__toggle ${this.props.toggleIconClass}`; - const isVisible = this.props.show || this.state.isVisible; - return ( - - - - - {isVisible ? ( -
-
-
{this.props.children}
-
- ) : null} - - ); - } -} - -SubPanel.propTypes = { - toggleIconClass: PropTypes.string.isRequired, - show: PropTypes.bool, -}; - -SubPanel.defaultProps = { - toggleIconClass: 'plotlyjs_editor__icon-cog', -}; diff --git a/src/components/containers/TraceAccordion.js b/src/components/containers/TraceAccordion.js index ae9cddfae..1b024861c 100644 --- a/src/components/containers/TraceAccordion.js +++ b/src/components/containers/TraceAccordion.js @@ -7,38 +7,34 @@ import {connectTraceToPlot} from '../../lib'; const TraceFold = connectTraceToPlot(Fold); export default class TraceAccordion extends Component { - constructor(props, context) { + constructor(props) { super(props); this.addTrace = this.addTrace.bind(this); - this.renderPanel = this.renderPanel.bind(this); - } - - renderPanel(d, i) { - return ( - - {this.props.children} - - ); } addTrace() { - this.context.onUpdate && + if (this.context.onUpdate) { this.context.onUpdate({ type: EDITOR_ACTIONS.ADD_TRACE, }); + } } render() { const data = this.context.data || []; return (
- {this.props.canAdd && ( + {this.props.canAdd ? ( Add - )} - {data.map(this.renderPanel)} + ) : null} + {data.map((d, i) => ( + + {this.props.children} + + ))}
); } @@ -50,5 +46,6 @@ TraceAccordion.contextTypes = { }; TraceAccordion.propTypes = { + children: PropTypes.node, canAdd: PropTypes.bool, }; diff --git a/src/components/containers/TraceMarkerSection.js b/src/components/containers/TraceMarkerSection.js new file mode 100644 index 000000000..287ea7665 --- /dev/null +++ b/src/components/containers/TraceMarkerSection.js @@ -0,0 +1,41 @@ +import Section from './Section'; +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import localize from '../../lib/localize'; + +class TraceMarkerSection extends Component { + constructor(props, context) { + super(props, context); + this.setLocals(context); + } + + componentWillReceiveProps(nextProps, nextContext) { + this.setLocals(nextContext); + } + + setLocals(context) { + const _ = this.props.localize; + const traceType = context.fullContainer.type; + if (traceType === 'bar') { + this.name = _('Bars'); + } else { + this.name = _('Points'); + } + } + + render() { + return
{this.props.children}
; + } +} + +TraceMarkerSection.propTypes = { + children: PropTypes.node, + localize: PropTypes.func, + name: PropTypes.string, +}; + +TraceMarkerSection.contextTypes = { + fullContainer: PropTypes.object, +}; + +export default localize(TraceMarkerSection); diff --git a/src/components/containers/__tests__/Layout-test.js b/src/components/containers/__tests__/Layout-test.js index 3597717fc..a24cadf30 100644 --- a/src/components/containers/__tests__/Layout-test.js +++ b/src/components/containers/__tests__/Layout-test.js @@ -3,13 +3,13 @@ import {Fold, Panel, Section} from '..'; import NumericInput from '../../widgets/NumericInputStatefulWrapper'; import React from 'react'; import {EDITOR_ACTIONS} from '../../../constants'; -import {TestEditor, fixtures, plotly} from '../../../lib/test-utils'; +import {TestEditor, fixtures} from '../../../lib/test-utils'; import {connectLayoutToPlot} from '../../../lib'; import {mount} from 'enzyme'; const Layouts = [Panel, Fold, Section].map(connectLayoutToPlot); const Editor = props => ( - + ); Layouts.forEach(Layout => { diff --git a/src/components/containers/__tests__/Section-test.js b/src/components/containers/__tests__/Section-test.js index 702d9e9d9..3bb47a7e0 100644 --- a/src/components/containers/__tests__/Section-test.js +++ b/src/components/containers/__tests__/Section-test.js @@ -1,8 +1,8 @@ import React from 'react'; import Section from '../Section'; -import SubPanel from '../SubPanel'; +import MenuPanel from '../MenuPanel'; import {Flaglist, Info, Numeric} from '../../fields'; -import {TestEditor, fixtures, plotly} from '../../../lib/test-utils'; +import {TestEditor, fixtures} from '../../../lib/test-utils'; import {connectTraceToPlot} from '../../../lib'; import {mount} from 'enzyme'; @@ -12,11 +12,7 @@ describe('Section', () => { it('is visible if it contains any visible children', () => { // mode is visible with scatter. Hole is not visible. Section should show. const wrapper = mount( - + { it('is visible if it contains any non attr children', () => { const wrapper = mount( - +
INFO
@@ -69,11 +61,7 @@ describe('Section', () => { it('is not visible if it contains no visible children', () => { // pull and hole are not scatter attrs. Section should not show. const wrapper = mount( - + @@ -91,20 +79,16 @@ describe('Section', () => { expect(wrapper.find(Numeric).exists()).toBe(false); }); - it('will render first subPanel even with no visible attrs', () => { + it('will render first menuPanel even with no visible attrs', () => { const wrapper = mount( - +
- + INFO - - + + MISINFORMATION - +
).find('[name="test-section"]'); diff --git a/src/components/containers/index.js b/src/components/containers/index.js index af6ee56e2..7284a63eb 100644 --- a/src/components/containers/index.js +++ b/src/components/containers/index.js @@ -1,7 +1,8 @@ -import SubPanel from './SubPanel'; +import MenuPanel from './MenuPanel'; import Fold from './Fold'; import Panel from './Panel'; import Section from './Section'; import TraceAccordion from './TraceAccordion'; +import TraceMarkerSection from './TraceMarkerSection'; -export {SubPanel, Fold, Panel, Section, TraceAccordion}; +export {MenuPanel, Fold, Panel, Section, TraceAccordion, TraceMarkerSection}; diff --git a/src/components/fields/AxesRange.js b/src/components/fields/AxesRange.js new file mode 100644 index 000000000..600b7cadb --- /dev/null +++ b/src/components/fields/AxesRange.js @@ -0,0 +1,29 @@ +import Numeric from './Numeric'; +import React, {Component} from 'react'; +import {connectToContainer} from '../../lib'; + +class AxesRange extends Component { + static modifyPlotProps(props, context, plotProps) { + if (!plotProps.isVisible) { + return; + } + const {fullContainer} = plotProps; + if (fullContainer && fullContainer.autorange) { + plotProps.isVisible = false; + } + } + + render() { + return ; + } +} + +AxesRange.propTypes = { + ...Numeric.propTypes, +}; + +AxesRange.defaultProps = { + showArrows: false, +}; + +export default connectToContainer(AxesRange); diff --git a/src/components/fields/AxesSelector.js b/src/components/fields/AxesSelector.js new file mode 100644 index 000000000..0cb7c84a0 --- /dev/null +++ b/src/components/fields/AxesSelector.js @@ -0,0 +1,52 @@ +import Field from './Field'; +import PropTypes from 'prop-types'; +import RadioBlocks from '../widgets/RadioBlocks'; +import React, {Component} from 'react'; +import {connectToContainer} from '../../lib'; + +export default class AxesSelector extends Component { + constructor(props, context) { + super(props, context); + + if (!props.axesTargetHandler && !context.axesTargetHandler) { + throw new Error( + 'AxesSelector must be nested within a connectAxesToPlot component ' + + 'or passed axesTargetHandler and axesOptions props' + ); + } + } + + render() { + let axesTargetHandler, axesOptions, axesTarget; + if (this.props.axesTargetHandler) { + axesTargetHandler = this.props.axesTargetHandler; + axesOptions = this.props.axesOptions; + axesTarget = this.props.axesTarget; + } else { + axesTargetHandler = this.context.axesTargetHandler; + axesOptions = this.context.axesOptions; + axesTarget = this.context.axesTarget; + } + return ( + + + + ); + } +} + +AxesSelector.propTypes = { + axesTargetHandler: PropTypes.func, + axesOptions: PropTypes.array, + axesTarget: PropTypes.string, +}; + +AxesSelector.contextTypes = { + axesTargetHandler: PropTypes.func, + axesOptions: PropTypes.array, + axesTarget: PropTypes.string, +}; diff --git a/src/components/fields/CanvasSize.js b/src/components/fields/CanvasSize.js new file mode 100644 index 000000000..23de502b1 --- /dev/null +++ b/src/components/fields/CanvasSize.js @@ -0,0 +1,25 @@ +import Numeric from './Numeric'; +import React, {Component} from 'react'; +import {connectToContainer} from '../../lib'; + +class CanvasSize extends Component { + static modifyPlotProps(props, context, plotProps) { + if (!plotProps.isVisible) { + return; + } + const {fullContainer} = plotProps; + if (fullContainer && fullContainer.autosize) { + plotProps.isVisible = false; + } + } + + render() { + return ; + } +} + +CanvasSize.propTypes = { + ...Numeric.propTypes, +}; + +export default connectToContainer(CanvasSize); diff --git a/src/components/fields/DataSelector.js b/src/components/fields/DataSelector.js index d5ce33f7b..7189b56bb 100644 --- a/src/components/fields/DataSelector.js +++ b/src/components/fields/DataSelector.js @@ -3,27 +3,27 @@ import PropTypes from 'prop-types'; import React, {Component} from 'react'; import Field from './Field'; import nestedProperty from 'plotly.js/src/lib/nested_property'; -import {bem, connectToContainer} from '../../lib'; +import {connectToContainer} from '../../lib'; function attributeIsData(meta = {}) { return meta.valType === 'data_array' || meta.arrayOk; } class DataSelector extends Component { - static unpackPlotProps(props, context, plotProps) { + static modifyPlotProps(props, context, plotProps) { if (attributeIsData(plotProps.attrMeta)) { plotProps.isVisible = true; } } - constructor(props, context) { + constructor(props) { super(props); - this.setLocals(props); this.updatePlot = this.updatePlot.bind(this); + this.setLocals(props); } - componentWillReceiveProps(nextProps) { + componentWillUpdate(nextProps) { this.setLocals(nextProps); } @@ -45,7 +45,9 @@ class DataSelector extends Component { updatePlot(value) { const attr = this.dataSrcExists ? this.srcAttr : this.props.attr; - this.props.updateContainer && this.props.updateContainer({[attr]: value}); + if (this.props.updateContainer) { + this.props.updateContainer({[attr]: value}); + } } render() { diff --git a/src/components/fields/Dropdown.js b/src/components/fields/Dropdown.js index 8a0e679f2..58a386b9c 100644 --- a/src/components/fields/Dropdown.js +++ b/src/components/fields/Dropdown.js @@ -2,10 +2,15 @@ import DropdownWidget from '../widgets/Dropdown'; import Field from './Field'; import PropTypes from 'prop-types'; import React, {Component} from 'react'; -import {bem, connectToContainer} from '../../lib'; +import {connectToContainer} from '../../lib'; export class UnconnectedDropdown extends Component { render() { + let placeholder; + if (this.props.multiValued) { + placeholder = this.props.fullValue(); + } + return ( ); diff --git a/src/components/fields/Field.js b/src/components/fields/Field.js index 4a5c2bdee..cd0c795bc 100644 --- a/src/components/fields/Field.js +++ b/src/components/fields/Field.js @@ -1,42 +1,65 @@ -import React, {Component} from 'react'; import PropTypes from 'prop-types'; -import {bem} from '../../lib'; +import React, {Component} from 'react'; +import MenuPanel from '../containers/MenuPanel'; +import classnames from 'classnames'; +import {bem, localize} from '../../lib'; +import {multiValueText} from '../../lib/constants'; -export default class Field extends Component { - render() { - let postfix = null; - if (this.props.postfix) { - postfix = ( -
-
- {this.props.postfix} -
-
- ); +class Field extends Component { + renderPostfix() { + if (!this.props.postfix) { + return null; } + return ( +
+
{this.props.postfix}
+
+ ); + } - if (!this.props.label) { - const noTitleModifier = this.props.center ? ['center'] : null; - return ( -
-
- {this.props.children} -
- {postfix} -
- ); + render() { + const { + center, + children, + label, + localize: _, + multiValued, + postfix, + } = this.props; + + let fieldClass; + if (!label) { + fieldClass = classnames('field__no-title', { + 'field__no-title--center': center, + }); + } else { + fieldClass = classnames('field__widget', { + 'field__widget--postfix': Boolean(postfix), + }); } - const widgetModifier = this.props.postfix ? ['postfix'] : null; return (
-
-
{this.props.label}
-
-
- {this.props.children} + {label ? ( +
+
{label}
+
+ ) : null} +
+ {children} + {multiValued ? ( + +
{_(multiValueText.title)}
+
{_(multiValueText.text)}
+
{_(multiValueText.subText)}
+
+ ) : null}
- {postfix} + {postfix ? ( +
+
{postfix}
+
+ ) : null}
); } @@ -45,9 +68,15 @@ export default class Field extends Component { Field.propTypes = { center: PropTypes.bool, label: PropTypes.string, + localize: PropTypes.func, postfix: PropTypes.string, + multiValued: PropTypes.bool, + children: PropTypes.node, }; Field.defaultProps = { center: false, + multiValued: false, }; + +export default localize(Field); diff --git a/src/components/fields/Info.js b/src/components/fields/Info.js index 0a431c1bb..a3cd6734f 100644 --- a/src/components/fields/Info.js +++ b/src/components/fields/Info.js @@ -1,5 +1,4 @@ import Field from './Field'; -import PropTypes from 'prop-types'; import React, {Component} from 'react'; export default class Info extends Component { diff --git a/src/components/fields/Numeric.js b/src/components/fields/Numeric.js index fa1222368..7a6a20496 100644 --- a/src/components/fields/Numeric.js +++ b/src/components/fields/Numeric.js @@ -2,7 +2,7 @@ import Field from './Field'; import NumericInput from '../widgets/NumericInputStatefulWrapper'; import PropTypes from 'prop-types'; import React, {Component} from 'react'; -import {bem, connectToContainer} from '../../lib'; +import {connectToContainer} from '../../lib'; class Numeric extends Component { render() { @@ -10,12 +10,13 @@ class Numeric extends Component { ); @@ -23,12 +24,18 @@ class Numeric extends Component { } Numeric.propTypes = { + defaultValue: PropTypes.number, fullValue: PropTypes.func, min: PropTypes.number, max: PropTypes.number, + showArrows: PropTypes.bool, step: PropTypes.number, updatePlot: PropTypes.func, ...Field.propTypes, }; +Numeric.defaultProps = { + showArrows: true, +}; + export default connectToContainer(Numeric); diff --git a/src/components/fields/Radio.js b/src/components/fields/Radio.js index d803905c2..e655363ce 100644 --- a/src/components/fields/Radio.js +++ b/src/components/fields/Radio.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React, {Component} from 'react'; import RadioBlocks from '../widgets/RadioBlocks'; import Field from './Field'; -import {bem, connectToContainer} from '../../lib'; +import {connectToContainer} from '../../lib'; class Radio extends Component { render() { diff --git a/src/components/fields/SymbolSelector.js b/src/components/fields/SymbolSelector.js new file mode 100644 index 000000000..2a58b1bb7 --- /dev/null +++ b/src/components/fields/SymbolSelector.js @@ -0,0 +1,58 @@ +import Field from './Field'; +import PropTypes from 'prop-types'; +import React, {Component} from 'react'; +import SymbolSelectorWidget from '../widgets/SymbolSelector'; +import nestedProperty from 'plotly.js/src/lib/nested_property'; +import {SYMBOLS} from '../../lib/constants'; +import {connectToContainer} from '../../lib'; + +class SymbolSelector extends Component { + render() { + const {fullContainer, fullValue, updatePlot} = this.props; + + const markerColor = nestedProperty(fullContainer, 'marker.color').get(); + const borderWidth = nestedProperty( + fullContainer, + 'marker.line.width' + ).get(); + + let borderColor = markerColor; + if (borderWidth) { + borderColor = nestedProperty(fullContainer, 'marker.line.color').get(); + } + + let symbolOptions; + if (this.props.is3D) { + symbolOptions = SYMBOLS.filter(option => { + return option.threeD; + }); + } else { + symbolOptions = [...SYMBOLS]; + } + + return ( + + + + ); + } +} + +SymbolSelector.propTypes = { + defaultValue: PropTypes.number, + fullValue: PropTypes.func, + updatePlot: PropTypes.func, + ...Field.propTypes, +}; + +SymbolSelector.defaultProps = { + showArrows: true, +}; + +export default connectToContainer(SymbolSelector); diff --git a/src/components/fields/TraceSelector.js b/src/components/fields/TraceSelector.js index 2032d6901..95dec45b7 100644 --- a/src/components/fields/TraceSelector.js +++ b/src/components/fields/TraceSelector.js @@ -4,14 +4,68 @@ import React, {Component} from 'react'; import nestedProperty from 'plotly.js/src/lib/nested_property'; import {connectToContainer} from '../../lib'; +function computeTraceOptionsFromSchema(schema) { + const capitalize = s => s.charAt(0).toUpperCase() + s.substring(1); + + // Filter out Polar "area" type as it is fairly broken and we want to present + // scatter with fill as an "area" chart type for convenience. + const traceTypes = Object.keys(schema.traces).filter(t => t !== 'area'); + + const labels = traceTypes.map(capitalize); + const traceOptions = traceTypes.map((t, i) => ({ + label: labels[i], + value: t, + })); + + const i = traceOptions.findIndex(opt => opt.value === 'scatter'); + traceOptions.splice( + i + 1, + 0, + {label: 'Line', value: 'line'}, + {label: 'Area', value: 'area'} + ); + + return traceOptions; +} + class TraceSelector extends Component { constructor(props, context) { super(props, context); this.updatePlot = this.updatePlot.bind(this); this.fullValue = this.fullValue.bind(this); - const scatterAttrs = this.context.plotSchema.traces.scatter.attributes; - this.fillTypes = scatterAttrs.fill.values.filter(v => v !== 'none'); + let fillMeta; + if (props.getValObject) { + fillMeta = props.getValObject('fill'); + } + if (fillMeta) { + this.fillTypes = fillMeta.values.filter(v => v !== 'none'); + } else { + this.fillTypes = [ + 'tozeroy', + 'tozerox', + 'tonexty', + 'tonextx', + 'toself', + 'tonext', + ]; + } + + this.setLocals(props, context); + } + + setLocals(props, context) { + if (props.traceOptions) { + this.traceOptions = props.traceOptions; + } else if (context.plotSchema) { + this.traceOptions = computeTraceOptionsFromSchema(context.plotSchema); + } else { + this.traceOptions = [{label: 'Scatter', value: 'scatter'}]; + } + } + + componentWillReceiveProps(nextProps, nextContext) { + this.setLocals(nextProps, nextContext); } updatePlot(value) { @@ -26,18 +80,19 @@ class TraceSelector extends Component { update = {type: value}; } - this.props.updateContainer && this.props.updateContainer(update); + if (this.props.updateContainer) { + this.props.updateContainer(update); + } } fullValue() { - const type = this.props.fullValue(); + const {container, fullValue} = this.props; + const type = fullValue(); - // we use gd.data instead of fullData so that we can show the trace - // even if the trace is not visible due to missing data. - // If we used fullData mode or fill will be undefined as the fullTrace - // isn't computed when not visible. - const mode = nestedProperty(this.props.trace, 'mode').get(); - const fill = nestedProperty(this.props.trace, 'fill').get(); + // If we used fullData mode or fill it may be undefined if the fullTrace + // is not visible and therefore does not have these values computed. + const mode = nestedProperty(container, 'mode').get(); + const fill = nestedProperty(container, 'fill').get(); if (type === 'scatter' && this.fillTypes.includes(fill)) { return 'area'; @@ -54,6 +109,7 @@ class TraceSelector extends Component { const props = Object.assign({}, this.props, { fullValue: this.fullValue, updatePlot: this.updatePlot, + options: this.traceOptions, }); return ; @@ -64,4 +120,11 @@ TraceSelector.contextTypes = { plotSchema: PropTypes.object, }; +TraceSelector.propTypes = { + getValObject: PropTypes.func, + container: PropTypes.object.isRequired, + fullValue: PropTypes.func.isRequired, + updateContainer: PropTypes.func, +}; + export default connectToContainer(TraceSelector); diff --git a/src/components/fields/index.js b/src/components/fields/index.js index a18b3f12f..2d3d9e199 100644 --- a/src/components/fields/index.js +++ b/src/components/fields/index.js @@ -1,3 +1,6 @@ +import AxesRange from './AxesRange'; +import AxesSelector from './AxesSelector'; +import CanvasSize from './CanvasSize'; import ColorPicker from './Color'; import Dropdown from './Dropdown'; import Flaglist from './Flaglist'; @@ -5,9 +8,13 @@ import Info from './Info'; import Radio from './Radio'; import DataSelector from './DataSelector'; import Numeric from './Numeric'; +import SymbolSelector from './SymbolSelector'; import TraceSelector from './TraceSelector'; export { + AxesRange, + AxesSelector, + CanvasSize, ColorPicker, Dropdown, Flaglist, @@ -15,5 +22,6 @@ export { Radio, DataSelector, Numeric, + SymbolSelector, TraceSelector, }; diff --git a/src/components/index.js b/src/components/index.js index 4bd7f548c..42ac7373e 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,4 +1,7 @@ import { + AxesRange, + AxesSelector, + CanvasSize, ColorPicker, Dropdown, Flaglist, @@ -6,14 +9,25 @@ import { Radio, DataSelector, Numeric, + SymbolSelector, TraceSelector, } from './fields'; -import {SubPanel, Fold, Panel, Section, TraceAccordion} from './containers'; +import { + MenuPanel, + Fold, + Panel, + Section, + TraceAccordion, + TraceMarkerSection, +} from './containers'; import PanelMenuWrapper from './PanelMenuWrapper'; export { - SubPanel, + AxesSelector, + AxesRange, + MenuPanel, + CanvasSize, ColorPicker, DataSelector, Dropdown, @@ -25,6 +39,8 @@ export { PanelMenuWrapper, Radio, Section, + SymbolSelector, TraceAccordion, + TraceMarkerSection, TraceSelector, }; diff --git a/src/components/widgets/EditableText.js b/src/components/widgets/EditableText.js index 7dd41616c..43f5ef9a8 100644 --- a/src/components/widgets/EditableText.js +++ b/src/components/widgets/EditableText.js @@ -89,8 +89,8 @@ EditableText.propTypes = { // Called on input keyDown events onKeyDown: PropTypes.func, - // Input value property - text: PropTypes.string, + // Input value property ... + text: PropTypes.any, // Input properties placeholder: PropTypes.string, diff --git a/src/components/widgets/NumericInput.js b/src/components/widgets/NumericInput.js deleted file mode 100644 index 7b7416413..000000000 --- a/src/components/widgets/NumericInput.js +++ /dev/null @@ -1,114 +0,0 @@ -import EditableText from "./EditableText"; -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import isNumeric from "fast-isnumeric"; -import classnames from "classnames"; - -export const UP_ARROW = 38; -export const DOWN_ARROW = 40; -export const TEST_SELECTOR_CLASS = "js-NumericInput"; - -export default class NumericInput extends Component { - constructor(props) { - super(props); - - this.onKeyDown = this.onKeyDown.bind(this); - this.incrementValue = this.incrementValue.bind(this); - } - - onKeyDown(e) { - switch (e.keyCode) { - case UP_ARROW: - return this.incrementValue("increase"); - case DOWN_ARROW: - return this.incrementValue("decrease"); - default: - break; - } - } - - incrementValue(direction) { - const step = this.props.step || 1; - let currentValue = this.props.value; - - if (isNumeric(this.props.value)) { - if (direction === "increase") { - currentValue = currentValue + step; - } else { - currentValue = currentValue - step; - } - } - - // incrementers blur the line between blur and onChange. - if (this.props.onUpdate) { - this.props.onUpdate(currentValue); - } else { - this.props.onChange(currentValue); - } - } - - renderArrows() { - if (!this.props.showArrows) { - return; - } - - return ( -
-
- -
-
- -
-
- ); - } - - render() { - const wrapperClassName = classnames("numeric-input__wrapper"); - - const editableClass = classnames( - "numeric-input__number", - this.props.editableClassName, - TEST_SELECTOR_CLASS - ); - - return ( -
- - {this.renderArrows()} -
- ); - } -} - -/*NumericInput.propTypes = { - value: customPropTypes.customOneOfType([ - PropTypes.string, - customPropTypes.isNumeric, - customPropTypes.isNull, - ]).isDefined, - onChange: PropTypes.func.isRequired, - onUpdate: PropTypes.func, - step: PropTypes.number, - showArrows: PropTypes.bool, - editableClassName: PropTypes.string, -};*/ - -NumericInput.defaultProps = { - showError: false, - showArrows: true, -}; diff --git a/src/components/widgets/NumericInputStatefulWrapper.js b/src/components/widgets/NumericInputStatefulWrapper.js index ea6a49aae..7d7f12e0d 100644 --- a/src/components/widgets/NumericInputStatefulWrapper.js +++ b/src/components/widgets/NumericInputStatefulWrapper.js @@ -1,191 +1,146 @@ -import NumericInput from "./NumericInput"; -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import isNumeric from "fast-isnumeric"; -import { MIXED_VALUES, MIXED_MODE_VALUE } from "../../lib/constants"; -import { clamp } from "../../lib"; - -// mapPropsToState, What is this absurdity?!? NumericInputStatefulWrapper -// maintains state so that users can muck around in the inner NumericInput -// input box. We don't want to fire updates() each time a user enters a -// character. Only when the user blurs do we want the update method to be fired. -// So why map props onto state? The internal state is mapped to the inputbox -// and with MIXED_VALUE mode we need a way to forcibly change the characters in -// the inputbox. So incoming props update state but the user is then permitted -// to make textual changes to the inputbox outside of the knowledge of the -// Store. Then onBlur we fire onUpdate and the Store can decide whether to keep -// the value the user inputed or change it to something else. There is also -// an edge case where we are in mixedMode and showing some special character in -// the inputbox "-" and the user tries to manually edit the input box with -// garbage and move on. To make it clear that we are still in mixedMode and that -// no other inputs have been changed we revert their garbage back to "-". -// This requires a setState inside the onUpdate method. -function mapPropsToState({ value: propValue, defaultValue = 0 }) { - let value; - const mixedMode = propValue === MIXED_VALUES; - - if (mixedMode) { - // MixedMode is useful when indicating to the user that there - // is another source of value coming from somewhere else in the - // app which renders this control optional. For example a user - // may have selected a value for xaxis range and is now exploring - // the UI for applying ranges to "all axes". In this case a - // mixedValue is shown so the user has some visual information that - // applying a value to "all axes" will somehow supercede some related - // value elsewhere. WS2 also provides a more helpful message in these - // cases than just the MIXED_MODE_VALUE - value = MIXED_MODE_VALUE; - } else if (propValue === null) { - // Null is used throughout the App to represent "no value." - // This may be an unfortunate decision but NumericInput supports - // null by showing the user that the value is actually - // "defaultValue" or 0. - // Actually it would be nice to take this chunk of code out. - value = defaultValue; - } else { - value = propValue; - } +import EditableText from './EditableText'; +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import isNumeric from 'fast-isnumeric'; - return { value, mixedMode }; -} +export const UP_ARROW = 38; +export const DOWN_ARROW = 40; export default class NumericInputStatefulWrapper extends Component { constructor(props) { super(props); - this.state = mapPropsToState(props); - + this.state = {value: props.value}; this.onChange = this.onChange.bind(this); - this.onUpdate = this.onUpdate.bind(this); + this.updateValue = this.updateValue.bind(this); } componentWillReceiveProps(nextProps) { if (nextProps.value !== this.state.value) { - this.setState(mapPropsToState(nextProps)); + this.setState({value: nextProps.value}); + } + } + + onKeyDown(e) { + switch (e.keyCode) { + case UP_ARROW: + this.incrementValue('increase'); + break; + case DOWN_ARROW: + this.incrementValue('decrease'); + break; + default: + break; } } onChange(value) { - /* - * Mixed Mode is preserved until new props are sent down from - * upstream components - */ - this.setState({ value }); + this.setState({value}); } - onUpdate(value) { - const { defaultValue, integerOnly, max, min } = this.props; - - // defaultValue is truthy or numeric (account for falsey 0) - const hasDefaultValue = defaultValue || isNumeric(defaultValue); - let updatedValue = value; - - // If we are in mixed mode and receive the placeholder value then - // the user is attempting to increment or decrement. If we are in - // mixed mode and receive some other value then the user has entered - // this value explicitly in the inputbox and is bluring away. - // In the case of incrementing and decrementing we set the updatedValue - // to the default value or min or 0. If the value is set explicitly and - // is numeric we do the same --- call onUpdate. This allows upstream - // components to send in new props that toggle this component out of - // mixedValue state. If it is set explicitly in the input box but is not - // numeric onUpdate is not called and mixedMode is maintained. - // In this case we also force MIXED_MODE_VALUE so the user is aware that - // no settings have actually been changed. - if (this.state.mixedMode && updatedValue === MIXED_MODE_VALUE) { - const fallbackValue = min || 0; - updatedValue = hasDefaultValue ? defaultValue : fallbackValue; - } else if (this.state.mixedMode && !isNumeric(updatedValue)) { - // mixed mode takes precedence over showing default values when - // empty strings are input. We return early to bypass that logic. - this.setState({ value: MIXED_MODE_VALUE }); + updateValue(newValue) { + const {max, min, integerOnly, value: propsValue} = this.props; + let updatedValue = newValue; + + // When the user blurs on non-numeric data reset the component + // to the last known good value (this.props.value). + if (!isNumeric(updatedValue)) { + this.setState({value: propsValue}); return; } - // If supplied a default value use it when the user blurs on an - // empty string or string made up of spaces. - if (typeof updatedValue === "string" && hasDefaultValue) { - updatedValue = updatedValue.replace(/^\s+/g, ""); - if (updatedValue.length === 0) { - updatedValue = defaultValue; - } + updatedValue = Number(updatedValue); + if (integerOnly) { + updatedValue = Math.floor(updatedValue); } - // When correct input is supplied by the user constrain it to be within - // [max, min] if max and min are supplied. Ditto for forcing an - // integer value. We take the floor instead of rounding - // as that is/(may be) less confusing to the user visually. - const numericBounds = isNumeric(min) && isNumeric(max); - if (isNumeric(updatedValue)) { - updatedValue = Number(updatedValue); + if (isNumeric(min)) { + updatedValue = Math.max(min, updatedValue); + } - if (integerOnly) { - updatedValue = Math.floor(updatedValue); - } + if (isNumeric(max)) { + updatedValue = Math.min(max, updatedValue); + } + + this.props.onUpdate(updatedValue); + } + + incrementValue(direction) { + const {defaultValue, min, step = 1} = this.props; + const {value} = this.state; - if (numericBounds) { - updatedValue = clamp(updatedValue, min, max); - } else if (isNumeric(min)) { - updatedValue = Math.max(min, updatedValue); - } else if (isNumeric(max)) { - updatedValue = Math.min(max, updatedValue); + let valueUpdate; + if (isNumeric(value)) { + if (direction === 'increase') { + valueUpdate = value + step; + } else { + valueUpdate = value - step; } + } else { + // if we are multi-valued and the user is incrementing or decrementing + // update with some sane value so we can "break" out of multi-valued mode. + if (isNumeric(defaultValue)) { + valueUpdate = defaultValue; + } else { + // TODO smarter handling depending if user decrements or increments? + valueUpdate = min || 0; + } + } + + // incrementers blur the line between blur and onChange. + this.updateValue(valueUpdate); + } - this.props.onUpdate(updatedValue); + renderArrows() { + if (!this.props.showArrows) { + return null; } + + return ( +
+
+ +
+
+ +
+
+ ); } render() { return ( - +
+ + {this.renderArrows()} +
); } } NumericInputStatefulWrapper.propTypes = { - // defaultValue is default value used when - // A) a user leaves the input empty or filled with spaces. - // B) a user is moving out of mixed mode. - // C) a `null` value is supplied to this component. defaultValue: PropTypes.number, editableClassName: PropTypes.string, - - // When integerOnly flag is set any numeric input supplied by - // the user is constrained to be a whole integer number. - // Math.floor is used for this operation. integerOnly: PropTypes.bool, - - // If min is supplied and defaultValue is *not* supplied the min - // value will be used when the user moves out of mixed mode. - // If both min and max are supplied they are used to constrain - // numeric input from the user to be within this range. max: PropTypes.number, min: PropTypes.number, - - // Handler run onBlur and called with the updated value. onUpdate: PropTypes.func.isRequired, - - // showArrows is a flag that will show or hide the increment and - // decrement buttons on the side of the inputbox. Defaults to true. showArrows: PropTypes.bool, - - // If incrementors are present step size controls the numeric step taken - // when incrementing and decrementing. step: PropTypes.number, - value: PropTypes.any, - /*value: customPropTypes.customOneOfType([ - PropTypes.string, - customPropTypes.isNumeric, - customPropTypes.isNull, - ]).isDefined,*/ }; NumericInputStatefulWrapper.defaultProps = { diff --git a/src/components/widgets/SymbolSelector.js b/src/components/widgets/SymbolSelector.js new file mode 100644 index 000000000..f96fcdbd5 --- /dev/null +++ b/src/components/widgets/SymbolSelector.js @@ -0,0 +1,143 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import tinyColor from 'tinycolor2'; +import ModalBox from '../containers/ModalBox'; + +const tooLightFactor = 0.8; + +function tooLight(color) { + const hslColor = tinyColor(color).toHsl(); + return hslColor.l > tooLightFactor; +} + +export default class SymbolSelector extends Component { + constructor(props) { + super(props); + this.state = { + isOpen: false, + }; + this.togglePanel = this.togglePanel.bind(this); + } + + shouldComponentUpdate(nextProps, nextState) { + const {markerColor, borderColor} = this.props; + const { + markerColor: nextMarkerColor, + borderColor: nextBorderColor, + } = nextProps; + + return ( + this.props.value !== nextProps.value || + this.state.isOpen !== nextState.isOpen || + markerColor !== nextMarkerColor || + borderColor !== nextBorderColor + ); + } + + togglePanel() { + this.setState({isOpen: !this.state.isOpen}); + } + + renderActiveOption() { + const {markerColor, borderColor, symbolOptions, value} = this.props; + const currentSymbol = symbolOptions.find(symbol => symbol.value === value); + if (!currentSymbol) { + return ( + + {'-'} + + ); + } + + const symbolStyle = { + stroke: currentSymbol.fill === 'none' ? markerColor : borderColor, + strokeOpacity: '1', + strokeWidth: '2px', + fill: currentSymbol.fill === 'none' ? 'none' : markerColor, + }; + + return ( + + + + + + + + ); + } + + renderOptions() { + const {markerColor, borderColor, symbolOptions} = this.props; + return symbolOptions.map(option => { + const {fill, value, label} = option; + + const symbolStyle = { + stroke: fill === 'none' ? markerColor : borderColor, + strokeOpacity: '1', + strokeWidth: '2px', + fill: fill === 'none' ? 'none' : markerColor, + }; + return ( +
+ + + + + +
+ ); + }); + } + + render() { + const {isOpen} = this.state; + const {markerColor} = this.props; + + // TODO link these colors into theme + const backgroundColor = tooLight(markerColor) ? '#bec8d9' : 'white'; + + return ( +
+
+ + {this.renderActiveOption()} + + + + +
+ {isOpen ? ( + + {this.renderOptions()} + + ) : null} +
+ ); + } +} + +SymbolSelector.propTypes = { + markerColor: PropTypes.string, + borderColor: PropTypes.string, + value: PropTypes.string, + onChange: PropTypes.func, + symbolOptions: PropTypes.array, +}; diff --git a/src/index.js b/src/index.js index 803873da8..15c16f022 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,7 @@ import Hub from './hub'; import PlotlyEditor from './PlotlyEditor'; import { localize, + connectAxesToLayout, connectLayoutToPlot, connectToContainer, connectTraceToPlot, @@ -9,7 +10,9 @@ import { import {EDITOR_ACTIONS} from './constants'; import { - SubPanel, + AxesRange, + AxesSelector, + CanvasSize, ColorPicker, DataSelector, Dropdown, @@ -22,12 +25,18 @@ import { PanelMenuWrapper, Radio, Section, + MenuPanel, + SymbolSelector, TraceAccordion, + TraceMarkerSection, TraceSelector, } from './components'; export { - SubPanel, + AxesRange, + AxesSelector, + MenuPanel, + CanvasSize, ColorPicker, DataSelector, Dropdown, @@ -42,8 +51,11 @@ export { PanelMenuWrapper, Radio, Section, + SymbolSelector, TraceAccordion, + TraceMarkerSection, TraceSelector, + connectAxesToLayout, connectLayoutToPlot, connectToContainer, connectTraceToPlot, diff --git a/src/lib/connectAxesToLayout.js b/src/lib/connectAxesToLayout.js new file mode 100644 index 000000000..2af048572 --- /dev/null +++ b/src/lib/connectAxesToLayout.js @@ -0,0 +1,238 @@ +import React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import nestedProperty from 'plotly.js/src/lib/nested_property'; +import {MULTI_VALUED} from './constants'; +import {getDisplayName, isPlainObject} from '../lib'; + +/** + * Simple replacer to use with JSON.stringify. + * @param {*} key Current object key. + * @param {*} value Current value in object at key. + * @returns {*} If we return undefined, the key is skipped in JSON.stringify. + */ +function skipPrivateKeys(key, value) { + if (key.startsWith('_')) { + return void 0; + } + + return value; +} + +/** + * Deep-copies the value using JSON. Underscored (private) keys are removed. + * @param {*} value Some nested value from the plotDiv object. + * @returns {*} A deepcopy of the value. + */ +function deepCopyPublic(value) { + if (typeof value === 'undefined') { + return value; + } + + return window.JSON.parse(window.JSON.stringify(value, skipPrivateKeys)); +} + +/* + * Test that we can connectLayoutToPlot(connectAxesToLayout(Panel)) + */ +function setMultiValuedContainer(intoObj, fromObj, key, config = {}) { + var intoVal = intoObj[key], + fromVal = fromObj[key]; + + var searchArrays = config.searchArrays; + + // don't merge private attrs + if ( + (typeof key === 'string' && key.charAt(0) === '_') || + typeof intoVal === 'function' || + key === 'module' + ) { + return; + } + + // already a mixture of values, can't get any worse + if (intoVal === MULTI_VALUED) { + return; + } else if (intoVal === void 0) { + // if the original doesn't have the key it's because that key + // doesn't do anything there - so use the new value + // note that if fromObj doesn't have a key in intoObj we will not + // attempt to merge them at all, so this behavior makes the merge + // independent of order. + intoObj[key] = fromVal; + } else if (key === 'colorscale') { + // colorscales are arrays... need to stringify before comparing + // (other vals we don't want to stringify, as differences could + // potentially be real, like 'false' and false) + if (String(intoVal) !== String(fromVal)) { + intoObj[key] = MULTI_VALUED; + } + } else if (Array.isArray(intoVal)) { + // in data, other arrays are data, which we don't care about + // for styling purposes + if (!searchArrays) { + return; + } + // in layout though, we need to recurse into arrays + for (var i = 0; i < fromVal.length; i++) { + setMultiValuedContainer(intoVal, fromVal, i, searchArrays); + } + } else if (isPlainObject(fromVal)) { + // recurse into objects + if (!isPlainObject(intoVal)) { + throw new Error('tried to merge object into non-object: ' + key); + } + Object.keys(fromVal).forEach(function(key2) { + setMultiValuedContainer(intoVal, fromVal, key2, searchArrays); + }); + } else if (isPlainObject(intoVal)) { + throw new Error('tried to merge non-object into object: ' + key); + } else if (intoVal !== fromVal) { + // different non-empty values - + intoObj[key] = MULTI_VALUED; + } +} + +function computeAxesOptions(fullContainer, axes) { + const options = [{label: 'All Axes', value: 'allaxes'}]; + for (let i = 0; i < axes.length; i++) { + const ax = axes[i]; + const axesPrefix = ax._id.length > 1 ? ' ' + ax._id.substr(1) : ''; + const label = `${ax._id.charAt(0).toUpperCase()} Axis${axesPrefix}`; + options[i + 1] = {label, value: ax._name}; + } + + return options; +} + +export default function connectAxesToLayout(WrappedComponent) { + class AxesConnectedComponent extends Component { + constructor(props, context) { + super(props, context); + + this.state = {axesTarget: this.props.defaultAxesTarget}; + this.axesTargetHandler = this.axesTargetHandler.bind(this); + this.updateContainer = this.updateContainer.bind(this); + + this.setLocals(props, this.state, context); + } + + componentWillUpdate(nextProps, nextState, nextContext) { + // This is not enough, what if plotly.js adds new axes... + this.setLocals(nextProps, nextState, nextContext); + } + + // This function should be optimized. We can compare a list of + // axesNames to nextAxesNames and check gd.layout[axesN] for shallow + // equality. Unfortunately we are currently mutating gd.layout so the + // shallow check is not possible. + setLocals(nextProps, nextState, nextContext) { + const {plotly, graphDiv, container, fullContainer} = nextContext; + const {axesTarget} = nextState; + if (plotly) { + this.axes = plotly.Axes.list(graphDiv); + } else { + this.axes = []; + } + this.axesOptions = computeAxesOptions(fullContainer, this.axes); + + if (axesTarget === 'allaxes') { + const multiValuedContainer = deepCopyPublic(this.axes[0]); + this.axes.slice(1).forEach(ax => + Object.keys(ax).forEach(key => + setMultiValuedContainer(multiValuedContainer, ax, key, { + searchArrays: true, + }) + ) + ); + this.fullContainer = multiValuedContainer; + this.defaultContainer = this.axes[0]; + // what should this be set to? Probably doesn't matter. + this.container = {}; + } else { + this.fullContainer = nestedProperty(fullContainer, axesTarget).get(); + this.container = nestedProperty(container, axesTarget).get(); + } + } + + getChildContext() { + return { + axesOptions: this.axesOptions, + axesTarget: this.state.axesTarget, + axesTargetHandler: this.axesTargetHandler, + container: this.container, + defaultContainer: this.defaultContainer, + fullContainer: this.fullContainer, + updateContainer: this.updateContainer, + }; + } + + axesTargetHandler(axesTarget) { + this.setState({axesTarget}); + } + + updateContainer(update) { + const newUpdate = {}; + const {axesTarget} = this.state; + + let axes = this.axes; + if (axesTarget !== 'allaxes') { + // only the selected container + axes = [this.fullContainer]; + } + + const keys = Object.keys(update); + for (let i = 0; i < keys.length; i++) { + for (let j = 0; j < axes.length; j++) { + const scene = axes[j]._id.substr(1); + let axesKey = axes[j]._name; + + // scenes are nested + if (scene.indexOf('scene') !== -1) { + axesKey = `${scene}.${axesKey}`; + } + + const newkey = `${axesKey}.${keys[i]}`; + newUpdate[newkey] = update[keys[i]]; + } + } + + this.context.updateContainer(newUpdate); + } + + render() { + return ; + } + } + + AxesConnectedComponent.displayName = `AxesConnected${getDisplayName( + WrappedComponent + )}`; + + AxesConnectedComponent.propTypes = { + defaultAxesTarget: PropTypes.string, + }; + + AxesConnectedComponent.defaultProps = { + defaultAxesTarget: 'xaxis', + }; + + AxesConnectedComponent.contextTypes = { + container: PropTypes.object.isRequired, + fullContainer: PropTypes.object.isRequired, + graphDiv: PropTypes.object.isRequired, + plotly: PropTypes.object, + updateContainer: PropTypes.func, + }; + + AxesConnectedComponent.childContextTypes = { + axesOptions: PropTypes.array, + axesTarget: PropTypes.string, + axesTargetHandler: PropTypes.func, + container: PropTypes.object, + defaultContainer: PropTypes.object, + fullContainer: PropTypes.object, + updateContainer: PropTypes.func, + }; + + return AxesConnectedComponent; +} diff --git a/src/lib/connectLayoutToPlot.js b/src/lib/connectLayoutToPlot.js index 3bbe3ea41..00e474c52 100644 --- a/src/lib/connectLayoutToPlot.js +++ b/src/lib/connectLayoutToPlot.js @@ -14,12 +14,17 @@ export default function connectLayoutToPlot(WrappedComponent) { getChildContext() { const {layout, fullLayout, plotly} = this.context; - return { - getValObject: attr => + + let getValObject; + if (plotly) { + getValObject = attr => plotly.PlotSchema.getLayoutValObject( fullLayout, nestedProperty({}, attr).parts - ), + ); + } + return { + getValObject, updateContainer: this.updateContainer, container: layout, fullContainer: fullLayout, @@ -48,7 +53,7 @@ export default function connectLayoutToPlot(WrappedComponent) { LayoutConnectedComponent.contextTypes = { layout: PropTypes.object, fullLayout: PropTypes.object, - plotly: PropTypes.object.isRequired, + plotly: PropTypes.object, onUpdate: PropTypes.func, }; diff --git a/src/lib/connectToContainer.js b/src/lib/connectToContainer.js index 6209b71a5..315c61250 100644 --- a/src/lib/connectToContainer.js +++ b/src/lib/connectToContainer.js @@ -5,6 +5,12 @@ import {getDisplayName} from '../lib'; export default function connectToContainer(WrappedComponent) { class ContainerConnectedComponent extends Component { + static modifyPlotProps(props, context, plotProps) { + if (WrappedComponent.modifyPlotProps) { + WrappedComponent.modifyPlotProps(props, context, plotProps); + } + } + constructor(props, context) { super(props, context); @@ -39,9 +45,9 @@ export default function connectToContainer(WrappedComponent) { ); if (props.isVisible) { return ; - } else { - return null; } + + return null; } } @@ -51,6 +57,7 @@ export default function connectToContainer(WrappedComponent) { ContainerConnectedComponent.contextTypes = { container: PropTypes.object, + defaultContainer: PropTypes.object, fullContainer: PropTypes.object, getValObject: PropTypes.func, updateContainer: PropTypes.func, diff --git a/src/lib/connectTraceToPlot.js b/src/lib/connectTraceToPlot.js index d581ded89..4d2c8120b 100644 --- a/src/lib/connectTraceToPlot.js +++ b/src/lib/connectTraceToPlot.js @@ -20,12 +20,18 @@ export default function connectTraceToPlot(WrappedComponent) { const trace = data[traceIndex] || {}; const fullTraceIndex = findFullTraceIndex(fullData, traceIndex); const fullTrace = fullData[fullTraceIndex] || {}; - return { - getValObject: attr => + + let getValObject; + if (plotly) { + getValObject = attr => plotly.PlotSchema.getTraceValObject( fullTrace, nestedProperty({}, attr).parts - ), + ); + } + + return { + getValObject, updateContainer: this.updateTrace, deleteContainer: this.deleteTrace, container: trace, @@ -70,7 +76,7 @@ export default function connectTraceToPlot(WrappedComponent) { TraceConnectedComponent.contextTypes = { fullData: PropTypes.array, data: PropTypes.array, - plotly: PropTypes.object.isRequired, + plotly: PropTypes.object, onUpdate: PropTypes.func, }; diff --git a/src/lib/constants.js b/src/lib/constants.js index 76226479f..10093cb01 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -1,4 +1,4 @@ -export const baseClass = "plotly-editor"; +export const baseClass = 'plotly-editor'; /* * Control represents multiple settings (like for several axes) @@ -8,447 +8,414 @@ export const baseClass = "plotly-editor"; * strings, we include a non-printable character (ESC) so it's not something * people could type. */ -export const MIXED_VALUES = "\x1bMIXED_VALUES"; +export const MULTI_VALUED = '\x1bMIXED_VALUES'; // how mixed values are represented in text inputs -export const MIXED_MODE_VALUE = "-"; - -/* -export const CLEAR_WORKSPACE = "WORKSPACE_CLEAR_WORKSPACE"; - -export const EDIT_MODE = { - ANALYSIS: "WORKSPACE_EDIT_MODE_ANALYSIS", - GRAPH: "WORKSPACE_EDIT_MODE_GRAPH", - STYLE: "WORKSPACE_EDIT_MODE_STYLE", - SHARE: "WORKSPACE_EDIT_MODE_SHARE", - EXPORT: "WORKSPACE_EDIT_MODE_EXPORT", - JSON: "WORKSPACE_EDIT_MODE_JSON", -}; - -export const STYLE_MODE = { - TRACES: "WORKSPACE_STYLE_MODE_TRACES", - LAYOUT: "WORKSPACE_STYLE_MODE_LAYOUT", - NOTES: "WORKSPACE_STYLE_MODE_NOTES", - AXES: "WORKSPACE_STYLE_MODE_AXES", - LEGEND: "WORKSPACE_STYLE_MODE_LEGEND", - COLOR_BARS: "WORKSPACE_STYLE_MODE_COLOR_BARS", - SHAPES: "WORKSPACE_STYLE_MODE_SHAPES", - MAPBOX_LAYERS: "WORKSPACE_STYLE_MODE_MAPBOX_LAYERS", - IMAGES: "WORKSPACE_STYLE_MODE_IMAGES", - MOBILE: "WORKSPACE_STYLE_MODE_MOBILE", -}; - -export const GRAPH_MODE = { - CREATE: "WORKSPACE_GRAPH_MODE_CREATE", - FILTER: "WORKSPACE_GRAPH_MODE_FILTER", - GROUPBY: "WORKSPACE_GRAPH_MODE_GROUPBY", +export const MULTI_VALUED_PLACEHOLDER = '---'; + +export const multiValueText = { + title: 'Multiple Values', + text: + 'This input has multiple values associated with it. ' + + 'Changing this setting will override these custom inputs.', + subText: + "Common Case: An 'All' tab might display this message " + + 'because the X and Y tabs contain different settings.', }; -export const ADD_PLOT_ID = "WORKSPACE_ADD_PLOT_ID"; -export const ADD_PLOT_SOURCE = "WORKSPACE_ADD_PLOT_SOURCE"; -export const ADD_PLOT_SOURCE_SHARE_KEY = "WORKSPACE_ADD_PLOT_SOURCE_SHARE_KEY"; -export const UPDATE_PLOT_DIRTY = "WORKSPACE_UPDATE_PLOT_DIRTY"; - -export const ADD_SHARE_FID = "WORKSPACE_ADD_SHARE_FID"; - -// Spec used to create the EditSidebar Buttons -export const EDIT_MODE_MENU_ITEMS = [ +export const SYMBOLS = [ { - mode: EDIT_MODE.GRAPH, - text: "Graph", + value: 'circle', + alias: 0, + label: 'M5,0A5,5 0 1,1 0,-5A5,5 0 0,1 5,0Z', + threeD: true, + gl: true, }, { - mode: EDIT_MODE.STYLE, - text: "Style", + value: 'circle-open', + alias: 0, + label: 'M5,0A5,5 0 1,1 0,-5A5,5 0 0,1 5,0Z', + fill: 'none', + threeD: true, + gl: true, }, { - mode: EDIT_MODE.ANALYSIS, - text: "Analysis", + value: 'circle-open-dot', + alias: 0, + label: 'M5,0A5,5 0 1,1 0,-5A5,5 0 0,1 5,0ZM0,0.5L0.5,0L0,-0.5L-0.5,0Z', + fill: 'none', }, + + {value: 'square', alias: 1, label: 'M5,5H-5V-5H5Z', threeD: true, gl: true}, { - mode: EDIT_MODE.JSON, - text: "JSON", + value: 'square-open', + alias: 1, + label: 'M5,5H-5V-5H5Z', + fill: 'none', + threeD: true, + gl: true, }, { - mode: EDIT_MODE.EXPORT, - text: "Export", + value: 'square-open-dot', + alias: 1, + label: 'M5,5H-5V-5H5ZM0,0.5L0.5,0L0,-0.5L-0.5,0Z', + fill: 'none', }, -]; -export const STYLE_MODE_MENU_ITEMS = [ { - mode: STYLE_MODE.TRACES, - text: "Traces", + value: 'diamond', + alias: 2, + label: 'M6.5,0L0,6.5L-6.5,0L0,-6.5Z', + threeD: true, + gl: true, }, { - mode: STYLE_MODE.LAYOUT, - text: "Layout", + value: 'diamond-open', + alias: 2, + label: 'M6.5,0L0,6.5L-6.5,0L0,-6.5Z', + fill: 'none', + threeD: true, + gl: true, }, { - mode: STYLE_MODE.NOTES, - text: "Notes", + value: 'diamond-open-dot', + alias: 2, + label: 'M6.5,0L0,6.5L-6.5,0L0,-6.5ZM0,0.5L0.5,0L0,-0.5L-0.5,0Z', + fill: 'none', }, + { - mode: STYLE_MODE.AXES, - text: "Axes", + value: 'cross', + alias: 3, + label: 'M6,2H2V6H-2V2H-6V-2H-2V-6H2V-2H6Z', + threeD: true, + gl: true, }, { - mode: STYLE_MODE.LEGEND, - text: "Legend", + value: 'cross-open', + alias: 3, + label: 'M6,2H2V6H-2V2H-6V-2H-2V-6H2V-2H6Z', + fill: 'none', + gl: true, }, + { - mode: STYLE_MODE.COLOR_BARS, - text: "Color Bars", + value: 'x', + alias: 4, + label: + 'M0,2.83l2.83,2.83l2.83,-2.83l-2.83,-2.83l2.83,-2.83l-2.83,-2.83l-2.83,2.83l-2.83,-2.83l-2.83,2.83l2.83,2.83l-2.83,2.83l2.83,2.83Z', + threeD: true, + gl: true, }, { - mode: STYLE_MODE.SHAPES, - text: "Shapes", + value: 'x-open', + alias: 4, + label: + 'M0,2.83l2.83,2.83l2.83,-2.83l-2.83,-2.83l2.83,-2.83l-2.83,-2.83l-2.83,2.83l-2.83,-2.83l-2.83,2.83l2.83,2.83l-2.83,2.83l2.83,2.83Z', + fill: 'none', + gl: true, }, + + {value: 'triangle-up', alias: 5, label: 'M-5.77,2.5H5.77L0,-5Z', gl: true}, { - mode: STYLE_MODE.MAPBOX_LAYERS, - text: "GeoJSON", + value: 'triangle-up-open', + alias: 5, + label: 'M-5.77,2.5H5.77L0,-5Z', + fill: 'none', + gl: true, }, + + {value: 'triangle-down', alias: 6, label: 'M-5.77,-2.5H5.77L0,5Z', gl: true}, { - mode: STYLE_MODE.IMAGES, - text: "Images", + value: 'triangle-down-open', + alias: 6, + label: 'M-5.77,-2.5H5.77L0,5Z', + fill: 'none', + gl: true, }, + + {value: 'triangle-left', alias: 7, label: 'M2.5,-5.77V5.77L-5,0Z', gl: true}, { - mode: STYLE_MODE.MOBILE, - text: "Mobile", + value: 'triangle-left-open', + alias: 7, + label: 'M2.5,-5.77V5.77L-5,0Z', + fill: 'none', + gl: true, }, -]; -export const GRAPH_MODE_MENU_ITEMS = [ + {value: 'triangle-right', alias: 8, label: 'M-2.5,-5.77V5.77L5,0Z', gl: true}, { - mode: GRAPH_MODE.CREATE, - text: "create", + value: 'triangle-right-open', + alias: 8, + label: 'M-2.5,-5.77V5.77L5,0Z', + fill: 'none', + gl: true, }, + + {value: 'triangle-ne', alias: 9, label: 'M-6,-3H3V6Z', gl: true}, { - mode: GRAPH_MODE.FILTER, - text: "filter", + value: 'triangle-ne-open', + alias: 9, + label: 'M-6,-3H3V6Z', + fill: 'none', + gl: true, }, + + {value: 'triangle-se', alias: 10, label: 'M3,-6V3H-6Z', gl: true}, { - mode: GRAPH_MODE.GROUPBY, - text: "group", + value: 'triangle-se-open', + alias: 10, + label: 'M3,-6V3H-6Z', + fill: 'none', + gl: true, }, -]; - -// temp / saved state of fids and column uids -export const UPDATE_COLUMN_ID_MAP = "WORKSPACE_UPDATE_COLUMN_ID_MAP"; -export const UPDATE_FID_MAP = "WORKSPACE_UPDATE_FID_MAP"; -export const UPDATE_LAST_SAVED = "WORKSPACE_UPDATE_LAST_SAVED"; -export const MARK_FID_AS_UNSAVED = "WORKSPACE_MARK_FID_AS_UNSAVED"; -export const REMOVE_COLUMN_IDS_FROM_COLUMN_ID_MAP = - "WORKSPACE_REMOVE_COLUMN_IDS_FROM_COLUMN_ID_MAP"; - -// panels -export const EDIT_MODES = Object.keys(EDIT_MODE).map(k => EDIT_MODE[k]); -export const STYLE_MODES = Object.keys(STYLE_MODE).map(k => STYLE_MODE[k]); -export const GRAPH_MODES = Object.keys(GRAPH_MODE).map(k => GRAPH_MODE[k]); -export const SELECT_EDIT_MODE = "WORKSPACE_SELECT_EDIT_MODE"; -export const SELECT_STYLE_MODE = "WORKSPACE_SELECT_STYLE_MODE"; -export const SELECT_GRAPH_MODE = "WORKSPACE_SELECT_GRAPH_MODE"; - -// figure -export const ADD_BREAKPOINT = "WORKSPACE_ADD_BREAKPOINT"; -export const DELETE_BREAKPOINT = "WORKSPACE_DELETE_BREAKPOINT"; -export const PLOTLY_RELAYOUT = "WORKSPACE_RELAYOUT"; -export const PLOTLY_RESTYLE = "WORKSPACE_RESTYLE"; -export const PLOTLY_NEW_PLOT = "WORKSPACE_NEW_PLOT"; -export const PLOTLY_ADD_FRAMES = "WORKSPACE_ADD_FRAMES"; -export const PLOTLY_DELETE_FRAMES = "WORKSPACE_DELETE_FRAMES"; -export const SELECT_FRAME = "WORKSPACE_SELECT_FRAME"; -export const SET_BASE_LAYOUT = "WORKSPACE_SET_BASE_LAYOUT"; -export const SET_BREAKPOINT = "WORKSPACE_SET_BREAKPOINT"; - -// annotations -export const INLINE_STYLE_LINK = "LINK"; -export const INLINE_STYLE_SUPER = "SUPERSCRIPT"; -export const INLINE_STYLE_SUB = "SUBSCRIPT"; - -// columns and tables -export const MERGE_COLUMNS_AND_TABLES = "WORKSPACE_MERGE_COLUMNS_AND_TABLES"; -export const UPDATE_TABLE = "UPDATE_TABLE"; -export const ADD_EMPTY_TABLE = "WORKSPACE_ADD_EMTPY_TABLE"; -export const SELECT_TABLE = "WORKSPACE_SELECT_TABLE"; -export const OVERWRITE_SOURCE = "WORKSPACE_OVERWRITE_SOURCE"; -export const REMOVE_TABLE = "WORKSPACE_REMOVE_TABLE"; -export const REMOVE_COLUMNS_FROM_TABLE = "WORKSPACE_REMOVE_COLUMNS_FROM_TABLE"; - -// encoding -export const ASSIGN_COLUMN = "WORKSPACE_ASSIGN_COLUMN"; -export const SWITCH_CHART_TYPE = "WORKSPACE_SWITCH_CHART_TYPE"; -export const NEW_ENCODING_LAYER = "WORKSPACE_NEW_ENCODING_LAYER"; -export const REMOVE_ENCODING_LAYER = "WORKSPACE_REMOVE_ENCODING_LAYER"; -export const SET_ENCODING = "WORKSPACE_SET_ENCODING"; -export const DEFAULT_ENCODING_TYPE = "scatter"; - -// analyses -export const UPDATE_ANALYSIS = "WORKSPACE_UPDATE_ANALYSIS"; -export const ADD_ANALYSIS = "WORKSPACE_ADD_ANALYSIS"; -export const REMOVE_ANALYSIS = "WORKSPACE_REMOVE_ANALYSIS"; -export const UPDATE_ANALYSIS_META = "WORKSPACE_UPDATE_ANALYSIS_META"; - -// HOT -export const MAX_HOT_ROWS = 5000; -export const CONTEXT_MENU_SOURCE = "WORKSPACE_CONTEXT_MENU_SOURCE"; -export const SORT_SOURCE = "WORKSPACE_SORT_SOURCE"; -export const INSERT_HEADERS_ABOVE_SOURCE = - "WORKSPACE_INSERT_HEADERS_ABOVE_SOURCE"; -export const CONTEXT_MENU_KEYS = { - CLEAR_HEADERS: "WORKSPACE_CONTEXT_MENU_KEYS_CLEAR_HEADERS", - COL_LEFT: "WORKSPACE_CONTEXT_MENU_KEYS_COL_LEFT", - COL_RIGHT: "WORKSPACE_CONTEXT_MENU_KEYS_COL_RIGHT", - INSERT_HEADERS_ABOVE: "WORKSPACE_CONTEXT_MENU_KEYS_INSERT_HEADERS_ABOVE", - REMOVE_COL: "WORKSPACE_CONTEXT_MENU_KEYS_REMOVE_COL", - REMOVE_ROW: "WORKSPACE_CONTEXT_MENU_KEYS_REMOVE_ROW", - RENAME_HEADER: "WORKSPACE_CONTEXT_MENU_KEYS_RENAME_HEADER", - RESET_SORT: "WORKSPACE_CONTEXT_MENU_KEYS_RESET_SORT", - ROW_ABOVE: "WORKSPACE_CONTEXT_MENU_KEYS_ROW_ABOVE", - ROW_BELOW: "WORKSPACE_CONTEXT_MENU_KEYS_ROW_BELOW", - SET_HEADERS: "WORKSPACE_CONTEXT_MENU_KEYS_SET_HEADERS", - SORT_ASCENDING: "WORKSPACE_CONTEXT_MENU_KEYS_SORT_ASCENDING", - SORT_DESCENDING: "WORKSPACE_CONTEXT_MENU_KEYS_SORT_DESCENDING", - TRANSPOSE_TABLE: "WORKSPACE_CONTEXT_MENU_KEYS_TRANSPOSE_TABLE", -}; - -// style panels -export const CONTROL_TYPES = { - FONT: "FONT", - COLOR: "COLOR", - ANCHOR_SELECTOR: "ANCHOR_SELECTOR", - DASH: "DASH", - SYMBOL: "SYMBOL", - RADIO: "RADIO", - TEXTAREA: "TEXTAREA", - ANNOTATION_EDITOR: "ANNOTATION_EDITOR", - MAPBOX_ACCESS_TOKEN: "MAPBOX_ACCESS_TOKEN", - NUMERIC_INPUT: "NUMERIC_INPUT", - FLAGLIST_CHECKBOX: "FLAGLIST_CHECKBOX", - COLUMN_INPUT: "COLUMN_INPUT", - COLOR_PALETTE: "COLOR_PALETTE", - DATETIME_INPUT: "DATETIME_INPUT", - DATETIME_DURATION: "DATETIME_DURATION", - DROPDOWN_SELECTOR: "DROPDOWN_SELECTOR", - DROPDOWN_WITH_TEXT_INPUT: "DROPDOWN_WITH_TEXT_INPUT", - RANGE: "RANGE", - SLIDER: "SLIDER", - INPUT_SLIDER: "INPUT_SLIDER", - BUTTON: "BUTTON", - RANGE_SELECTOR_BUTTONS: "RANGE_SELECTOR_BUTTONS", - TEXT_INPUT: "TEXT_INPUT", - UPLOAD_SHAPE_FILE: "UPLOAD_SHAPE_FILE", - UPLOAD_IMAGE_FILE: "UPLOAD_IMAGE_FILE", - REF_CONTROL: "REF_CONTROL", - ANCHOR: "ANCHOR", - MAPBOX_STYLE_URL: "MAPBOX_STYLE_URL", - ORIENTATION: "ORIENTATION", - NOTE: "NOTE", - ARROW: "ARROW", -}; - -// encoding panel -export const ENCODING_ATTRIBUTE_TYPES = { - PLOTLYJS: "PLOTLYJS", - ENCODING: "ENCODING", -}; - -// import modal -export const IMPORT_MODES = { - EXAMPLES: "EXAMPLES", - SQL: "SQL", - URL: "URL", - UPLOAD: "UPLOAD", -}; -export const ORG_IMPORT_MODES = { - DATASET: "DATASET", - NOTEBOOK: "NOTEBOOK", - PRESENTATION: "PRESENTATION", -}; - -export const IMPORT_EXAMPLE_URL = - "https://raw.githubusercontent.com/plotly/datasets/master/iris.csv"; - -export const CHART_TYPE_ICON = { - animation: "icon-animation", - area: "icon-plot-area", - bar: "icon-plot-bar", - box: "icon-plot-box", - candlestick: "icon-candlestick", - cartesianArea: "icon-plot-area", - choropleth: "icon-choropleth", - contour: "icon-contour", - errorbars: "icon-error-bars", - heatmap: "icon-plot-heatmap", - histogram2d: "icon-plot-2d-hist", - histogram2dcontour: "icon-plot-2d-hist", - histogram: "icon-plot-hist", - line: "icon-plot-line", - mesh3d: "icon-mesh3d", - ohlc: "icon-ohlc", - pie: "icon-pie-chart", - scatter3d: "icon-plot-3d-scatter", - line3d: "icon-plot-3d-line", - scatter: "icon-plot-scatter", - scattergeo: "icon-scatter-chart", - scattermapbox: "icon-scatter-chart", - scatterternary: "icon-ternary-scatter", - surface: "icon-plot-3d-surface", - timeseries: "icon-time-series", -}; + {value: 'triangle-sw', alias: 11, label: 'M6,3H-3V-6Z', gl: true}, + { + value: 'triangle-sw-open', + alias: 11, + label: 'M6,3H-3V-6Z', + fill: 'none', + gl: true, + }, -export const ALL_AXES = [ - { - axisTypeIdentifier: "AxesSpec", - typeQuery: "cartesian", - identifier: "xaxis", - defaultAxis: "xaxis", - options: [ - { value: "allaxes", label: "All", title: "All Axes" }, - { value: "xaxis", label: "X", title: "X Axes", singular: "X axis" }, - { value: "yaxis", label: "Y", title: "Y Axes", singular: "Y axis" }, - ], - }, - { - axisTypeIdentifier: "AxesSpec", - typeQuery: "gl2d", - identifier: "xaxis", - defaultAxis: "xaxis", - options: [ - { value: "allaxes", label: "All", title: "All Axes" }, - { value: "xaxis", label: "X", title: "X Axes", singular: "X axis" }, - { value: "yaxis", label: "Y", title: "Y Axes", singular: "Y axis" }, - ], - }, - { - axisTypeIdentifier: "GeoSpec", - typeQuery: "geo", - identifier: "geo", - defaultAxis: "lonaxis", - options: [ - { value: "lataxis", label: "Latitude", title: "Latitude" }, - { value: "lonaxis", label: "Longitude", title: "Longitude" }, - ], - }, - { - axisTypeIdentifier: "SceneSpec", - typeQuery: "gl3d", - identifier: "scene", - defaultAxis: "xaxis", - options: [ - { value: "allaxes", label: "All", title: "All Axes" }, - { value: "xaxis", label: "X", title: "X Axes", singular: "X axis" }, - { value: "yaxis", label: "Y", title: "Y Axes", singular: "Y axis" }, - { value: "zaxis", label: "Z", title: "Z Axes", singular: "Z axis" }, - ], - }, - { - axisTypeIdentifier: "TernarySpec", - typeQuery: "ternary", - identifier: "ternary", - defaultAxis: "aaxis", - options: [ - { value: "aaxis", label: "A", title: "A Axes", singular: "A Axis" }, - { value: "baxis", label: "B", title: "B Axes", singular: "B Axis" }, - { value: "caxis", label: "C", title: "C Axes", singular: "C Axis" }, - ], - }, - { - typeQuery: "pie", - options: "NO_AXES", + {value: 'triangle-nw', alias: 12, label: 'M-3,6V-3H6Z', gl: true}, + { + value: 'triangle-nw-open', + alias: 12, + label: 'M-3,6V-3H6Z', + fill: 'none', + gl: true, }, -]; -// Layout specification for CategorizedSelectTrace -export const CHART_CATEGORY = { - BUSINESS: "BUSINESS", - SCIENCE: "SCIENCE", - CHARTS_3D: "CHARTS_3D", - FINANCIAL: "FINANCIAL", - STATISTICS: "STATISTICS", - MAPS: "MAPS", -}; + { + value: 'pentagon', + alias: 13, + label: 'M4.76,-1.54L2.94,4.05H-2.94L-4.76,-1.54L0,-5Z', + gl: true, + }, + { + value: 'pentagon-open', + alias: 13, + label: 'M4.76,-1.54L2.94,4.05H-2.94L-4.76,-1.54L0,-5Z', + fill: 'none', + gl: true, + }, -export const CATEGORY_LAYOUT = [ - { category: CHART_CATEGORY.BUSINESS, label: "Business" }, - { category: CHART_CATEGORY.SCIENCE, label: "Science" }, - { category: CHART_CATEGORY.CHARTS_3D, label: "3d charts" }, - { category: CHART_CATEGORY.FINANCIAL, label: "Finance" }, - { category: CHART_CATEGORY.STATISTICS, label: "Statistics" }, - { category: CHART_CATEGORY.MAPS, label: "Maps" }, -]; + { + value: 'hexagon', + alias: 14, + label: 'M4.33,-2.5V2.5L0,5L-4.33,2.5V-2.5L0,-5Z', + gl: true, + }, + { + value: 'hexagon-open', + alias: 14, + label: 'M4.33,-2.5V2.5L0,5L-4.33,2.5V-2.5L0,-5Z', + fill: 'none', + gl: true, + }, -// ShareModalTabs -export const SHARE_MODAL_TAB_OPTIONS = { - LINK_AND_PRIVACY: "Link & Privacy", - COLLABORATORS: "Collaborate", - EMBED: "Embed", -}; + { + value: 'hexagon2', + alias: 15, + label: 'M-2.5,4.33H2.5L5,0L2.5,-4.33H-2.5L-5,0Z', + gl: true, + }, + { + value: 'hexagon2-open', + alias: 15, + label: 'M-2.5,4.33H2.5L5,0L2.5,-4.33H-2.5L-5,0Z', + fill: 'none', + gl: true, + }, -export const SHARE_MODAL_TABS = Object.keys(SHARE_MODAL_TAB_OPTIONS).map(k => { - return SHARE_MODAL_TAB_OPTIONS[k]; -}); + { + value: 'octagon', + alias: 16, + label: + 'M-1.92,-4.62H1.92L4.62,-1.92V1.92L1.92,4.62H-1.92L-4.62,1.92V-1.92Z', + }, + { + value: 'octagon-open', + alias: 16, + label: + 'M-1.92,-4.62H1.92L4.62,-1.92V1.92L1.92,4.62H-1.92L-4.62,1.92V-1.92Z', + fill: 'none', + }, -// 1x1px transparent gif: https://css-tricks.com/snippets/html/base64-encode-of-1x1px-transparent-gif/ -export const IMAGE_PLACEHOLDER = - "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"; + { + value: 'star', + alias: 17, + label: + 'M1.58,-2.16H6.66L2.54,0.83L4.12,5.66L0,2.67L-4.12,5.66L-2.54,0.83L-6.66,-2.16H-1.58L0,-7Z', + gl: true, + }, + { + value: 'star-open', + alias: 17, + label: + 'M1.58,-2.16H6.66L2.54,0.83L4.12,5.66L0,2.67L-4.12,5.66L-2.54,0.83L-6.66,-2.16H-1.58L0,-7Z', + fill: 'none', + gl: true, + }, -export const MAPBOX_ERROR_TYPES = { - INVALID_JSON: "INVALID_JSON", - FAILED_REQUEST: "FAILED_REQUEST", - FAILED_PARSING: "FAILED_PARSING", - UNKNOWN: "UNKNOWN", -}; + { + value: 'hexagram', + alias: 18, + label: + 'M-3.8,0l-1.9,-3.3h3.8l1.9,-3.3l1.9,3.3h3.8l-1.9,3.3l1.9,3.3h-3.8l-1.9,3.3l-1.9,-3.3h-3.8Z', + }, + { + value: 'hexagram-open', + alias: 18, + label: + 'M-3.8,0l-1.9,-3.3h3.8l1.9,-3.3l1.9,3.3h3.8l-1.9,3.3l1.9,3.3h-3.8l-1.9,3.3l-1.9,-3.3h-3.8Z', + fill: 'none', + }, -export const WORKSPACE_PLOT_ID = "js-main-plotly-workspace-plot"; + { + value: 'star-triangle-up', + alias: 19, + label: + 'M-6.93,4A 20,20 0 0 1 6.93,4A 20,20 0 0 1 0,-8A 20,20 0 0 1 -6.93,4Z', + }, + { + value: 'star-triangle-up-open', + alias: 19, + label: + 'M-6.93,4A 20,20 0 0 1 6.93,4A 20,20 0 0 1 0,-8A 20,20 0 0 1 -6.93,4Z', + fill: 'none', + }, -export const WORKSPACE_CONTAINER = document.getElementById("main"); -export const WORKSPACE_PLACEHOLDER = document.getElementById( - "placeholderworkspace" -); + { + value: 'star-triangle-down', + alias: 20, + label: + 'M6.93,-4A 20,20 0 0 1 -6.93,-4A 20,20 0 0 1 0,8A 20,20 0 0 1 6.93,-4Z', + }, + { + value: 'star-triangle-down-open', + alias: 20, + label: + 'M6.93,-4A 20,20 0 0 1 -6.93,-4A 20,20 0 0 1 0,8A 20,20 0 0 1 6.93,-4Z', + fill: 'none', + }, -const IS_TRANSFORM = true; + { + value: 'star-square', + alias: 21, + label: + 'M-5.5,-5.5A 10,10 0 0 1 -5.5,5.5A 10,10 0 0 1 5.5,5.5A 10,10 0 0 1 5.5,-5.5A 10,10 0 0 1 -5.5,-5.5Z', + }, + { + value: 'star-square-open', + alias: 21, + label: + 'M-5.5,-5.5A 10,10 0 0 1 -5.5,5.5A 10,10 0 0 1 5.5,5.5A 10,10 0 0 1 5.5,-5.5A 10,10 0 0 1 -5.5,-5.5Z', + fill: 'none', + }, -// quadruplet containing [CONSTANT_NAME TYPE LABEL IS_TRANSFORM]. -export const ANALYSES = [ - [ - "DESCRIPTIVE", - "descriptive-statistics", - "Descriptive statistics", - !IS_TRANSFORM, - ], - ["ANOVA_TEST", "anova", "ANOVA", !IS_TRANSFORM], - ["CHI_SQUARED_TEST", "chi-squared-test", "Chi-squared test", !IS_TRANSFORM], - ["T_TEST", "t-test", "T-test (two-tailed, independent)", !IS_TRANSFORM], - ["CORRELATION", "column-correlation", "Column correlation", !IS_TRANSFORM], - ["FIT", "fit", "Curve fitting", IS_TRANSFORM], - ["AVERAGE", "average", "Average", IS_TRANSFORM], - ["MOVING_AVERAGE", "moving-average", "Moving average", IS_TRANSFORM], -]; + { + value: 'star-diamond', + alias: 22, + label: + 'M-7,0A 9.5,9.5 0 0 1 0,7A 9.5,9.5 0 0 1 7,0A 9.5,9.5 0 0 1 0,-7A 9.5,9.5 0 0 1 -7,0Z', + }, + { + value: 'star-diamond-open', + alias: 22, + label: + 'M-7,0A 9.5,9.5 0 0 1 0,7A 9.5,9.5 0 0 1 7,0A 9.5,9.5 0 0 1 0,-7A 9.5,9.5 0 0 1 -7,0Z', + fill: 'none', + }, -export const ANALYSES_TYPES = ANALYSES.reduce((accum, [name, type]) => { - accum[name] = type; - return accum; -}, {}); + { + value: 'diamond-tall', + alias: 23, + label: 'M0,7L3.5,0L0,-7L-3.5,0Z', + gl: true, + }, + { + value: 'diamond-tall-open', + alias: 23, + label: 'M0,7L3.5,0L0,-7L-3.5,0Z', + fill: 'none', + gl: true, + }, -export const ANALYSES_TYPES_TO_LABELS = ANALYSES.reduce( - (accum, [, type, label]) => { - accum[type] = label; - return accum; + {value: 'diamond-wide', alias: 24, label: 'M0,3.5L7,0L0,-3.5L-7,0Z'}, + { + value: 'diamond-wide-open', + alias: 24, + label: 'M0,3.5L7,0L0,-3.5L-7,0Z', + fill: 'none', }, - {} -); -// used by WorkspaceActions to search for linked transforms inside traces -export const TRANSFORM_TYPES = ANALYSES.filter( - ([, , , isTransform]) => isTransform -).map(([, type]) => type); + {value: 'hourglass', alias: 25, label: 'M5,5H-5L5,-5H-5Z'}, + {value: 'bowtie', alias: 26, label: 'M5,5V-5L-5,5V-5Z', gl: true}, + { + value: 'cross-thin-open', + alias: 33, + label: 'M0,7V-7M7,0H-7', + fill: 'none', + gl: true, + }, + { + value: 'x-thin-open', + alias: 34, + label: 'M5,5L-5,-5M5,-5L-5,5', + fill: 'none', + }, + { + value: 'asterisk-open', + alias: 35, + label: 'M0,6V-6M6,0H-6M4.25,4.25L-4.25,-4.25M4.25,-4.25L-4.25,4.25', + fill: 'none', + gl: true, + }, -// Constants relating to the user interface + { + value: 'hash-open', + alias: 36, + label: 'M2.5,5V-5m-5,0V5M5,2.5H-5m0,-5H5', + fill: 'none', + }, + { + value: 'hash-open-dot', + alias: 36, + label: 'M2.5,5V-5m-5,0V5M5,2.5H-5m0,-5H5M0,0.5L0.5,0L0,-0.5L-0.5,0Z', + fill: 'none', + }, -export const RETURN_KEY = "Enter"; -export const ESCAPE_KEY = "Escape"; -export const COMMAND_KEY = "Meta"; -export const CONTROL_KEY = "Control"; -*/ + { + value: 'y-up-open', + alias: 37, + label: 'M-6,4L0,0M6,4L0,0M0,-8L0,0', + fill: 'none', + gl: true, + }, + { + value: 'y-down-open', + alias: 38, + label: 'M-6,-4L0,0M6,-4L0,0M0,8L0,0', + fill: 'none', + gl: true, + }, + { + value: 'y-left-open', + alias: 39, + label: 'M4,6L0,0M4,-6L0,0M-8,0L0,0', + fill: 'none', + }, + { + value: 'y-right-open', + alias: 40, + label: 'M-4,6L0,0M-4,-6L0,0M8,0L0,0', + fill: 'none', + }, + {value: 'line-ew-open', alias: 41, label: 'M7,0H-7', fill: 'none', gl: true}, + {value: 'line-ns-open', alias: 42, label: 'M0,7V-7', fill: 'none', gl: true}, + {value: 'line-ne-open', alias: 43, label: 'M5,-5L-5,5', fill: 'none'}, + {value: 'line-nw-open', alias: 44, label: 'M5,5L-5,-5', fill: 'none'}, +]; diff --git a/src/lib/index.js b/src/lib/index.js index 91359c36d..3de73e8f7 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -1,11 +1,12 @@ import bem, {icon} from './bem'; +import connectAxesToLayout from './connectAxesToLayout'; import connectLayoutToPlot from './connectLayoutToPlot'; import connectToContainer from './connectToContainer'; import connectTraceToPlot from './connectTraceToPlot'; import dereference from './dereference'; import findFullTraceIndex from './findFullTraceIndex'; import localize, {localizeString} from './localize'; -import walkObject, {makeAttrSetterPath} from './walkObject'; +import walkObject, {isPlainObject} from './walkObject'; function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); @@ -18,6 +19,7 @@ function getDisplayName(WrappedComponent) { export { bem, clamp, + connectAxesToLayout, connectLayoutToPlot, connectToContainer, connectTraceToPlot, @@ -25,8 +27,8 @@ export { getDisplayName, findFullTraceIndex, icon, + isPlainObject, localize, localizeString, - makeAttrSetterPath, walkObject, }; diff --git a/src/lib/unpackPlotProps.js b/src/lib/unpackPlotProps.js index 197a321c3..1d53e5b0f 100644 --- a/src/lib/unpackPlotProps.js +++ b/src/lib/unpackPlotProps.js @@ -1,55 +1,82 @@ import nestedProperty from 'plotly.js/src/lib/nested_property'; import isNumeric from 'fast-isnumeric'; -import findFullTraceIndex from './findFullTraceIndex'; +import {MULTI_VALUED, MULTI_VALUED_PLACEHOLDER} from './constants'; export default function unpackPlotProps(props, context, ComponentClass) { - const {updateContainer, container, fullContainer} = context; + const { + container, + getValObject, + defaultContainer, + fullContainer, + updateContainer, + } = context; if (!container || !fullContainer) { throw new Error( - `${ComponentClass.name} must be nested within a , ` + - 'or container' + `${ComponentClass.name} must be nested within a component connected ` + + 'to a plotly.js container.' ); } // Property accessors and meta information: const fullProperty = nestedProperty(fullContainer, props.attr); - const property = nestedProperty(container, props.attr); - const fullValue = () => fullProperty.get(); + const fullValue = () => { + const fv = fullProperty.get(); + if (fv === MULTI_VALUED) { + return MULTI_VALUED_PLACEHOLDER; + } + return fv; + }; + + let defaultValue = props.defaultValue; + if (defaultValue === void 0 && defaultContainer) { + defaultValue = nestedProperty(defaultContainer, props.attr).get(); + } // Property descriptions and meta: - const attrMeta = context.getValObject(props.attr) || {}; + let attrMeta; + if (getValObject) { + attrMeta = context.getValObject(props.attr) || {}; + } // Update data functions: const updatePlot = v => updateContainer && updateContainer({[props.attr]: v}); - // Visibility: + // Visibility and multiValues: + const fv = fullProperty.get(); + const multiValued = fv === MULTI_VALUED; + let isVisible = false; - const fv = fullValue(); - if (props.show || (fv !== undefined && fv !== null)) { + if (props.show || (fv !== void 0 && fv !== null)) { isVisible = true; } const plotProps = { attrMeta, container, + defaultValue, + getValObject, fullContainer, fullValue, isVisible, updateContainer, updatePlot, + multiValued, }; - if (isNumeric(attrMeta.max)) { - plotProps.max = attrMeta.max; - } - if (isNumeric(attrMeta.min)) { - plotProps.min = attrMeta.min; + if (attrMeta) { + if (isNumeric(attrMeta.max)) { + plotProps.max = attrMeta.max; + } + if (isNumeric(attrMeta.min)) { + plotProps.min = attrMeta.min; + } } - // Allow Component Classes to further augment plotProps: - ComponentClass.unpackPlotProps && - ComponentClass.unpackPlotProps(props, context, plotProps); + // Give Component Classes the space to modify plotProps: + if (ComponentClass.modifyPlotProps) { + ComponentClass.modifyPlotProps(props, context, plotProps); + } return plotProps; } diff --git a/src/lib/walkObject.js b/src/lib/walkObject.js index 2304defda..64edfea16 100644 --- a/src/lib/walkObject.js +++ b/src/lib/walkObject.js @@ -1,4 +1,4 @@ -function isPlainObject(input) { +export function isPlainObject(input) { return input && !Array.isArray(input) && typeof input === 'object'; } diff --git a/src/shame.js b/src/shame.js new file mode 100644 index 000000000..d6936d532 --- /dev/null +++ b/src/shame.js @@ -0,0 +1,12 @@ +/* + * DELETE THIS FILE AND EVERYTHING IN IT. + */ +import {list} from 'plotly.js/src/plots/cartesian/axis_ids'; + +export function noShame(opts) { + if (opts.plotly && !opts.plotly.Axes) { + opts.plotly.Axes = { + list, + }; + } +} diff --git a/src/styles/_helpers.scss b/src/styles/_helpers.scss index 749b9cdce..80b462bc2 100644 --- a/src/styles/_helpers.scss +++ b/src/styles/_helpers.scss @@ -1,3 +1,3 @@ .\+flex { display: flex; } .\+cursor-clickable { cursor: pointer; } -.\+hover-grey:hover{ color: $dark-grey; } +.\+hover-grey:hover{ color: $color-active-text; } diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss index 03e1800af..c633e6ef9 100644 --- a/src/styles/_mixins.scss +++ b/src/styles/_mixins.scss @@ -21,3 +21,15 @@ @error "'#{$value}' is not a valid z-index value"; } } + +@mixin clearfix() { + &:before, + &:after { + content: " "; + display: table; + } + + &:after { + clear: both; + } +} diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss deleted file mode 100644 index ef82f1079..000000000 --- a/src/styles/_variables.scss +++ /dev/null @@ -1,37 +0,0 @@ -/* - * SIZING AND WEIGHTS - */ -$font-weight-light: 300; -$font-weight-semibold: 600; -$font-size-small: 12px; -$h5-size: 16px !default; - -/* - * SPACING - */ -$base-spacing-unit: 24px !default; -$half-spacing-unit: 12px; -$quarter-spacing-unit: 6px; -$sixth-spacing-unit: 4px; -$eighth-spacing-unit: 3px; - -/* - * BORDERS - */ -$border-radius: 2px; -$border-radius-5: 5px; - -/* - * COLORS - */ -$color-white: #ffffff; -$lightgrey: #dcdddd; -$color-muted-text: #777; -$dark-grey: #4c5059; -$color-page-background: #f3f6fa; -$color-highlight-darker: #bec8d9; - -/* - * COMPONENT - */ -$fold-border: 1px solid $color-highlight-darker; diff --git a/src/styles/components/containers/_fold.scss b/src/styles/components/containers/_fold.scss index 4d1616e77..c91f1f9a8 100644 --- a/src/styles/components/containers/_fold.scss +++ b/src/styles/components/containers/_fold.scss @@ -15,8 +15,8 @@ clear: both; padding: 6px 12px; color: #ffffff; - font-size: 13px; - line-height: 13px; + font-size: $font-size; + line-height: $font-size; border: 1px solid #a2b1c6; background-color: #a2b1c6; height: 15px; diff --git a/src/styles/components/containers/_info.scss b/src/styles/components/containers/_info.scss new file mode 100644 index 000000000..812e6c2cf --- /dev/null +++ b/src/styles/components/containers/_info.scss @@ -0,0 +1,23 @@ +.info__title { + color: $color-link-light; + font-size: $font-size-medium; + font-weight: $font-weight-normal; + line-height: 18px; + padding: $half-spacing-unit $half-spacing-unit $quarter-spacing-unit $half-spacing-unit; +} + +.info__text { + padding: $quarter-spacing-unit $half-spacing-unit; + color: $color-secondary-text; + font-size: $font-size-small; + font-weight: $font-weight-light; + line-height: 18px; +} + +.info__sub-text { + color: $color-brand; + font-size: $font-size-small; + font-weight: $font-weight-light; + line-height: 14px; + padding: $quarter-spacing-unit $half-spacing-unit $half-spacing-unit $half-spacing-unit; +} diff --git a/src/styles/components/containers/_main.scss b/src/styles/components/containers/_main.scss index 34bd1a01f..93140140e 100644 --- a/src/styles/components/containers/_main.scss +++ b/src/styles/components/containers/_main.scss @@ -1,3 +1,6 @@ @import "panel"; @import "fold"; @import "section"; +@import "menupanel"; +@import "info"; +@import "modalbox"; diff --git a/src/styles/components/containers/_menupanel.scss b/src/styles/components/containers/_menupanel.scss new file mode 100644 index 000000000..34992627b --- /dev/null +++ b/src/styles/components/containers/_menupanel.scss @@ -0,0 +1,21 @@ +.menupanel { + padding-top: 0; +} + +.menupanel--ownline { + padding-top: 6px; +} + +.menupanel__icon-span { + font-size: $font-size-small; +} + +.menupanel__icon-span--question { + color: $color-link-light; +} + +.menupanel__icon { + padding-left: 6px; + padding-right: 6px; + vertical-align: middle; +} diff --git a/src/styles/components/containers/_modalbox.scss b/src/styles/components/containers/_modalbox.scss new file mode 100644 index 000000000..cd1f7faad --- /dev/null +++ b/src/styles/components/containers/_modalbox.scss @@ -0,0 +1,27 @@ +.modalbox { + position: absolute; + border-radius: 2px; + text-transform: none; + text-align: left; + border: 1px solid $color-border; + + align-content: center; + box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.2); + left: 0; + right: 0; + + margin: 0 24px; + min-width: 200px; + background-color: $color-white; + + @include z-index("cloud"); +} + +.modalbox__cover { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + @include z-index("underground"); +} diff --git a/src/styles/components/containers/_panel.scss b/src/styles/components/containers/_panel.scss index f707b35ce..e1c87e408 100644 --- a/src/styles/components/containers/_panel.scss +++ b/src/styles/components/containers/_panel.scss @@ -5,43 +5,9 @@ bottom: 5px; right: 1px; margin-left: 100px; - background-color: $color-page-background; + background-color: $color-panel-background; overflow-y: scroll; padding-left: $half-spacing-unit; padding-right: $half-spacing-unit; padding-top: $half-spacing-unit; } - -.subpanel__toggle { - padding-left: 12px; - padding-right: 12px; - vertical-align: middle; -} - -.subpanel{ - position: absolute; - border-radius: 2px; - text-transform: none; - text-align: left; - border: $fold-border; - - align-content: center; - box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.2); - left: 0; - right: 0; - - margin: 0 24px; - min-width: 200px; - background-color: $color-white; - - @include z-index("cloud"); -} - -.subpanel__cover { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - @include z-index("underground"); -} diff --git a/src/styles/components/containers/_section.scss b/src/styles/components/containers/_section.scss index ff736cba1..d85c61b17 100644 --- a/src/styles/components/containers/_section.scss +++ b/src/styles/components/containers/_section.scss @@ -1,9 +1,10 @@ .section__heading { - font-size: 13px; - color: #69738a; + display: flex; + font-size: $font-size; + color: $color-header-text; font-weight: 400; cursor: default; - background-color: #F4FAFF; + background-color: $color-section-header-background; padding: 6px 12px; clear: both; text-transform: capitalize; diff --git a/src/styles/components/fields/_field.scss b/src/styles/components/fields/_field.scss new file mode 100644 index 000000000..ef5a68dd0 --- /dev/null +++ b/src/styles/components/fields/_field.scss @@ -0,0 +1,59 @@ +.field { + align-items: center; + border-top: 1px solid #f8f8f9; + color: #6D6D6D; + display: flex; + font-size: $font-size; + font-weight: $font-weight-light; + justify-content: flex-start; + line-height: $font-size; + min-height: 32px; + padding: 6px 0; + width: 100%; +} + +.field:not(:last-child) { + border-bottom: 1px solid #f8f8f9; +} + +.field__no-title { + width: 100%; + padding: 0 12px; +} + +.field__no-title--center { + text-align: center; +} + +.field__title { + padding-left: 0.5em; + color: #69738a; + font-size: 14px; + width: 36%; + display: inline-block; +} + +.field__widget { + width: 64%; + padding-left: 12px; + display: inline-block; + padding-right: 12px; +} + +.field__widget--postfix { + width: 50%; + padding-right: 0px; +} + +.field__title { + width: 30%; + padding-left: 12px; + display: inline-block; + line-height: 18px; + text-transform: capitalize; +} + +.field__title-text { + text-transform: capitalize; + cursor: default; +} diff --git a/src/styles/components/fields/_main.scss b/src/styles/components/fields/_main.scss index c55ebee7d..3c7187a9b 100644 --- a/src/styles/components/fields/_main.scss +++ b/src/styles/components/fields/_main.scss @@ -1,59 +1,2 @@ -.field { - Align-items: center; - border-top: 1px solid #f8f8f9; - color: #6D6D6D; - display: flex; - font-size: 13px; - font-weight: 300; - justify-content: flex-start; - line-height: 13px; - min-height: 32px; - padding: 6px 0; - width: 100%; -} - -.field:not(:last-child) { - border-bottom: 1px solid #f8f8f9; -} - -.field__no-title { - width: 100%; - padding: 0 12px; -} - -.field__no-title--center { - text-align: center; -} - -.field__title { - padding-left: 0.5em; - color: #69738a; - font-size: 14px; - width: 36%; - display: inline-block; -} - -.field__widget { - width: 64%; - padding-left: 18px; - display: inline-block; - padding-right: 12px; -} - -.field__widget--postfix { - width: 50%; - padding-right: 0px; -} - -.field__title { - width: 30%; - padding-left: 12px; - display: inline-block; - line-height: 18px; - text-transform: capitalize; -} - -.field__title-text { - text-transform: capitalize; - cursor: default; -} +@import 'field'; +@import 'symbolselector'; diff --git a/src/styles/components/fields/_symbolselector.scss b/src/styles/components/fields/_symbolselector.scss new file mode 100644 index 000000000..029ae848e --- /dev/null +++ b/src/styles/components/fields/_symbolselector.scss @@ -0,0 +1,38 @@ +.symbol-selector__toggle { + border: 1px solid $color-border; + border-radius: 4px; + width: 80px; + cursor: pointer; + padding: $quarter-spacing-unit 10px; + @include clearfix(); +} + +.symbol-selector__toggle__option { + float: left; +} + +.symbol-selector__toggle__caret { + float: right; + color: $color-secondary-text; +} + +.symbol-selector__menu { + max-width: 225px; + position: absolute; + @include z-index("orbit"); + border: 1px solid $color-border; + padding: $font-size; + box-shadow: 2px 2px $font-size $color-border-secondary; +border-radius: $border-radius; +left: $base-spacing-unit; +} + + .symbol-selector__item { + display: inline; + } + + .symbol-selector__symbol { + &:hover { + background-color: $color-border; + } + } diff --git a/src/styles/components/widgets/_colorpicker.scss b/src/styles/components/widgets/_colorpicker.scss index 0a9244c43..05f762586 100644 --- a/src/styles/components/widgets/_colorpicker.scss +++ b/src/styles/components/widgets/_colorpicker.scss @@ -1,6 +1,5 @@ $swatch-size: 20px; $colorpicker-width: 250px; -$colorpicker-border-color: #bbb; $saturation-picker-height: 160px; $slider-picker-height: 10px; @@ -43,7 +42,7 @@ $slider-picker-height: 10px; width: 32px; height: 32px; border-radius: 50%; - border: 1px solid $lightgrey; + border: 1px solid $color-border-secondary; vertical-align: middle; padding-top: $sixth-spacing-unit; padding-left: $sixth-spacing-unit; @@ -52,7 +51,7 @@ $slider-picker-height: 10px; .colorpicker__selected-color { margin-left: $half-spacing-unit; - color: $color-muted-text; + color: $color-text; font-weight: $font-weight-light; font-size: $font-size-small; display: inline-block; @@ -67,17 +66,11 @@ $slider-picker-height: 10px; vertical-align: middle; } -.toolLabel { - color: $color-muted-text; - font-size: $font-size-small; - font-weight: $font-weight-light; -} - %colorpicker__component { position: relative; overflow: hidden; margin: 0 $half-spacing-unit; - border: 1px solid $colorpicker-border-color; + border: 1px solid $color-border; border-radius: $border-radius / 2; cursor: pointer; } diff --git a/src/styles/components/widgets/_numeric-input.scss b/src/styles/components/widgets/_numeric-input.scss index 8b9a4b066..853bf29c7 100644 --- a/src/styles/components/widgets/_numeric-input.scss +++ b/src/styles/components/widgets/_numeric-input.scss @@ -5,7 +5,7 @@ .numeric-input__number { display: inline-block; - border: 1px solid #bec8d9; + border: 1px solid $color-border; background: white; cursor: text; overflow: hidden; @@ -14,7 +14,7 @@ text-align: left; border-radius: 2px; padding: 6px 6px 6px 12px; - width: 80px; + width: 62px; vertical-align: middle; font-size: inherit; color: inherit; @@ -39,8 +39,8 @@ background-color: #f8f8f9; border: 1px solid #bec8d9; border-radius: 1px; - width: 13px; - height: 13px; + width: $font-size; + height: $font-size; line-height: 12px; text-align: center; } diff --git a/src/styles/icons/_icons.scss b/src/styles/icons/_icons.scss index 4d5405727..359fc8139 100644 --- a/src/styles/icons/_icons.scss +++ b/src/styles/icons/_icons.scss @@ -1,6 +1,6 @@ @font-face { font-family: "react-plotlyjs-editor"; - src: url('data:font/truetype;charset=utf-8;base64,AAEAAAANAIAAAwBQRkZUTXiTFR4AAAcoAAAAHEdERUYAMQAGAAAHCAAAACBPUy8yT9ZcgQAAAVgAAABWY21hcADUA2AAAAHAAAABSmdhc3D//wADAAAHAAAAAAhnbHlmun9ICgAAAxgAAAFsaGVhZA1zs0sAAADcAAAANmhoZWED5QIFAAABFAAAACRobXR4BgAAJQAAAbAAAAAQbG9jYQC2AAAAAAMMAAAACm1heHAASACBAAABOAAAACBuYW1ltk1ZOgAABIQAAAJJcG9zdHBwA2YAAAbQAAAALgABAAAAAQAAKqfLtV8PPPUACwIAAAAAANYquScAAAAA1iq5JwAlACUB2wHbAAAACAACAAAAAAAAAAEAAAHbAAAALgIAAAAAAAHbAAEAAAAAAAAAAAAAAAAAAAAEAAEAAAAEAH4AAgAAAAAAAgAAAAEAAQAAAEAAAAAAAAAAAQIAAZAABQAIAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAIABQkAAAAAAAAAAAABAAAAAAAAAAAAAAAAUGZFZABAAGEAYQHg/+AALgHb/9sAAAABAAAAAAAAAgAAAAAAAAACAAAAAgAAJQAAAAMAAAADAAAAHAABAAAAAABEAAMAAQAAABwABAAoAAAABgAEAAEAAgAAAGH//wAAAAAAYf//AAD/ogABAAAAAAAAAAABBgAAAQAAAAAAAAABAgAAAAIAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtgAAAAIAJQAlAdsB2wALAH0AACQ0JyYiBwYUFxYyPwEVFAcGDwEGBxYXFhQHBgcGIyIvAQYHBgcGKwEiJyY1JyYnBwYiJyYnJjU0Nz4BNz4BNyYvASInJj0BNDc2PwE2NyYnJjQ3Njc2MzIfATY3Njc2OwEyFxYVFxYXNzYyFxYXFhUUBw4BBw4BBxYfATIXFgFJFRY8FhUVFjwWpwIBBTQHBRMMAwMIFBYFBAMoCBIEBAEJQAUCAwgSCCgDCAMlCgICAQoDBAoCBwU0BQECAgEENQUHDxADAwgUFQYEAygIEgQEAQlABQIDCBIIKAMIAycIAgIBCgMECgIIBDQFAQLiPBYVFRY8FhUVUz8FAgICCBEJGA8DCAMKFRQDHwUGIxIIAgMDNQYEHgMDIg4CBQQCAg0EBA4DDBAIAwIFPwUCAgIIDgwUEwQGBAwSFQMfBQYjEggCAwM1BgQeAwMkDQEFBAICDQQEDwIODggDAgAAAAwAlgABAAAAAAABABUALAABAAAAAAACAA8AYgABAAAAAAADADIA2AABAAAAAAAEABUBNwABAAAAAAAFAAsBZQABAAAAAAAGABUBnQADAAEECQABACoAAAADAAEECQACAB4AQgADAAEECQADAGQAcgADAAEECQAEACoBCwADAAEECQAFABYBTQADAAEECQAGACoBcQByAGUAYQBjAHQALQBwAGwAbwB0AGwAeQBqAHMALQBlAGQAaQB0AG8AcgAAcmVhY3QtcGxvdGx5anMtZWRpdG9yAABwAGwAbwB0AGwAeQBqAHMALQBlAGQAaQB0AG8AcgAAcGxvdGx5anMtZWRpdG9yAABGAG8AbgB0AEYAbwByAGcAZQAgADIALgAwACAAOgAgAHIAZQBhAGMAdAAtAHAAbABvAHQAbAB5AGoAcwAtAGUAZABpAHQAbwByACAAOgAgADEAMAAtADEAMQAtADIAMAAxADcAAEZvbnRGb3JnZSAyLjAgOiByZWFjdC1wbG90bHlqcy1lZGl0b3IgOiAxMC0xMS0yMDE3AAByAGUAYQBjAHQALQBwAGwAbwB0AGwAeQBqAHMALQBlAGQAaQB0AG8AcgAAcmVhY3QtcGxvdGx5anMtZWRpdG9yAABWAGUAcgBzAGkAbwBuACAAMQAuADAAAFZlcnNpb24gMS4wAAByAGUAYQBjAHQALQBwAGwAbwB0AGwAeQBqAHMALQBlAGQAaQB0AG8AcgAAcmVhY3QtcGxvdGx5anMtZWRpdG9yAAAAAAACAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAQAAAABAAIBAgNjb2cAAAAAAAH//wACAAEAAAAOAAAAGAAAAAAAAgABAAMAAwABAAQAAAACAAAAAAABAAAAAMw9os8AAAAA1iq5JwAAAADWKrkn') format("truetype"); + src: url('data:font/truetype;charset=utf-8;base64,AAEAAAANAIAAAwBQRkZUTXiiALwAAAj8AAAAHEdERUYANAAGAAAI3AAAACBPUy8yT9ZchAAAAVgAAABWY21hcATcCWAAAAHIAAABSmdhc3D//wADAAAI1AAAAAhnbHlmzKQSxgAAAyQAAAMIaGVhZA2CnukAAADcAAAANmhoZWED5QIFAAABFAAAACRobXR4BpYAkwAAAbAAAAAWbG9jYQIQAsAAAAMUAAAAEG1heHAATACBAAABOAAAACBuYW1ltk1ZRAAABiwAAAJJcG9zdIZC6M8AAAh4AAAAWgABAAAAAQAAzn6fwV8PPPUACwIAAAAAANYyLvYAAAAA1jIu9gAlACUB2wHbAAAACAACAAAAAAAAAAEAAAHbAAAALgIAAAAAAAHbAAEAAAAAAAAAAAAAAAAAAAAEAAEAAAAHAH4AAwAAAAAAAgAAAAEAAQAAAEAAAAAAAAAAAQIAAZAABQAIAUwBZgAAAEcBTAFmAAAA9QAZAIQAAAIABQkAAAAAAAAAAAABAAAAAAAAAAAAAAAAUGZFZABAAGEAZAHg/+AALgHb/9sAAAABAAAAAAAAAgAAAAAAAAACAAAAAgAAJQAlAG4AcQAAAAAAAwAAAAMAAAAcAAEAAAAAAEQAAwABAAAAHAAEACgAAAAGAAQAAQACAAAAZP//AAAAAABh//8AAP+iAAEAAAAAAAAAAAEGAAABAAAAAAAAAAECAAAAAgAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwQFBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC2ATwBWgGEAAIAJQAlAdsB2wALAH0AACQ0JyYiBwYUFxYyPwEVFAcGDwEGBxYXFhQHBgcGIyIvAQYHBgcGKwEiJyY1JyYnBwYiJyYnJjU0Nz4BNz4BNyYvASInJj0BNDc2PwE2NyYnJjQ3Njc2MzIfATY3Njc2OwEyFxYVFxYXNzYyFxYXFhUUBw4BBw4BBxYfATIXFgFJFRY8FhUVFjwWpwIBBTQHBRMMAwMIFBYFBAMoCBIEBAEJQAUCAwgSCCgDCAMlCgICAQoDBAoCBwU0BQECAgEENQUHDxADAwgUFQYEAygIEgQEAQlABQIDCBIIKAMIAycIAgIBCgMECgIIBDQFAQLiPBYVFRY8FhUVUz8FAgICCBEJGA8DCAMKFRQDHwUGIxIIAgMDNQYEHgMDIg4CBQQCAg0EBA4DDBAIAwIFPwUCAgIIDgwUEwQGBAwSFQMfBQYjEggCAwM1BgQeAwMkDQEFBAICDQQEDwIODggDAgADACUAJQHbAdsAEwBIAFwAACU1NCcmKwEiBwYdARQXFjsBMjc2NzQmJyYjIgcGHwEWMzI3Njc2MzIWFRQHBgcGBwYdARQXFjsBMjc2NTQ3Njc2NzY3Njc2Nz4BFAcGBwYiJyYnJjQ3Njc2MhcWFwElAwMENgQDAwMEAzYDBANJIBgZF0YkBAYmAgMGAQsOCw0OFgYIDBMODwMDBDYEAwMGBgkIBgYHBgcFAwRtHR4yMHwwMh4dHR4yMHwwMh53NwQCAwMCBDcEAwICA8QYLAwLPQYGHAIEDgwHEAkLBggFCBEQEwsEAgMDAgQGCAkGBAQDBwQJCQkJFXwwMh4dHR4yMHwwMh4dHR4yAAAAAQBuAKUBkgFJAA8AAAAUDwEGIi8BJjQ3NjMhMhcBkgWABRAFgAUFBwYBAAYHAT0MB4AFBYAHDAcFBQAAAQBxAJ8BjwFFABgAAAEUDwEGIi8BJjU0PwE2MzIfATc2MzIfARYBjwOFAwgDhQMDDwIEAwRwcAQDBAIPAwEuBAOFAwOFAwQDAw8CAnFxAgIPAwAAAAAMAJYAAQAAAAAAAQAVACwAAQAAAAAAAgAPAGIAAQAAAAAAAwAyANgAAQAAAAAABAAVATcAAQAAAAAABQALAWUAAQAAAAAABgAVAZ0AAwABBAkAAQAqAAAAAwABBAkAAgAeAEIAAwABBAkAAwBkAHIAAwABBAkABAAqAQsAAwABBAkABQAWAU0AAwABBAkABgAqAXEAcgBlAGEAYwB0AC0AcABsAG8AdABsAHkAagBzAC0AZQBkAGkAdABvAHIAAHJlYWN0LXBsb3RseWpzLWVkaXRvcgAAcABsAG8AdABsAHkAagBzAC0AZQBkAGkAdABvAHIAAHBsb3RseWpzLWVkaXRvcgAARgBvAG4AdABGAG8AcgBnAGUAIAAyAC4AMAAgADoAIAByAGUAYQBjAHQALQBwAGwAbwB0AGwAeQBqAHMALQBlAGQAaQB0AG8AcgAgADoAIAAxADUALQAxADEALQAyADAAMQA3AABGb250Rm9yZ2UgMi4wIDogcmVhY3QtcGxvdGx5anMtZWRpdG9yIDogMTUtMTEtMjAxNwAAcgBlAGEAYwB0AC0AcABsAG8AdABsAHkAagBzAC0AZQBkAGkAdABvAHIAAHJlYWN0LXBsb3RseWpzLWVkaXRvcgAAVgBlAHIAcwBpAG8AbgAgADEALgAwAABWZXJzaW9uIDEuMAAAcgBlAGEAYwB0AC0AcABsAG8AdABsAHkAagBzAC0AZQBkAGkAdABvAHIAAHJlYWN0LXBsb3RseWpzLWVkaXRvcgAAAAAAAgAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAHAAAAAQACAQIBAwEEAQUDY29nD3F1ZXN0aW9uLWNpcmNsZQpjYXJldC1kb3duCmFuZ2xlLWRvd24AAAAAAAH//wACAAEAAAAOAAAAGAAAAAAAAgABAAMABgABAAQAAAACAAAAAAABAAAAAMw9os8AAAAA1jIu9gAAAADWMi72') format("truetype"); font-weight: normal; font-style: normal; } @@ -34,3 +34,12 @@ .plotlyjs_editor__icon-cog:before { content: "\61"; } +.plotlyjs_editor__icon-question-circle:before { + content: "\62"; +} +.plotlyjs_editor__.icon-caret-down:before { + content: "\63"; +} +.plotlyjs_editor__.icon-angle-down:before { + content: "\64"; +} diff --git a/src/styles/main.scss b/src/styles/main.scss index e84c6aa38..69b3c3c8e 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -1,5 +1,5 @@ @charset 'utf-8'; -@import 'variables'; +@import 'variables/main'; @import 'mixins'; @import 'helpers'; @import 'icons/main'; diff --git a/src/styles/variables/_defaults.scss b/src/styles/variables/_defaults.scss new file mode 100644 index 000000000..15971946e --- /dev/null +++ b/src/styles/variables/_defaults.scss @@ -0,0 +1,90 @@ +/* + * SIZING AND WEIGHTS + */ +$default-font-weight-light: 300; +$default-font-weight-normal: 400; +$default-font-weight-semibold: 600; +$default-font-size: 13px; +$default-font-size-small: 12px; +$default-font-size-medium: 14px; +$default-h5-size: 16px !default; + +/* + * SPACING + */ +$default-base-spacing-unit: 24px !default; +$default-half-spacing-unit: 12px; +$default-quarter-spacing-unit: 6px; +$default-sixth-spacing-unit: 4px; +$default-eighth-spacing-unit: 3px; + +/* + * BORDERS + */ +$default-border-radius: 2px; +$default-border-radius-5: 5px; + +/* + * COLORS + */ +$default-color-black-dark: #000; +$default-color-black-paleish: #333; +$default-color-black-pale: #666; +$default-color-black: #111; +$default-color-navyblue: #284576; +$default-color-blue: #119DFF; +$default-color-blue-dark: darken($default-color-blue, 20%); +$default-color-blue-pale: #45a9ff; +$default-color-blue-paleish: #2391fe; +$default-color-cyan-pale: #9bd1ff; +$default-color-gold: #f7bd0c; +$default-color-gray-blue: #69738a; +$default-color-gray-blue-pale: #bec8d9; +$default-color-gray-dark: #888; +$default-color-gray-light: #ddd; +$default-color-gray: #ccc; +$default-color-green-cyan: #53cf9d; +$default-color-green-pale: #dff0d8; +$default-color-green: #3c763d; +$default-color-red-pale: #f2dede; +$default-color-red: #a94442; +$default-color-white-blue: #f4faff; +$default-color-white-dark: #eee; +$default-color-white-darkish: #f8f8f8; +$default-color-white: #ffffff; + +// --- +// Text and Backgrounds +// --- +$default-color-rhino-core: #2a3f5f; // Active state text, emphasized text +$default-color-rhino-dark: #506784; // Default text color +$default-color-rhino-medium-1: #a2b1c6; +$default-color-rhino-medium-2: #c8d4e3; // Border Default +$default-color-rhino-light-1: #dfe8f3; // Border Secondary +$default-color-rhino-light-2: #ebf0f8; // Button Secondary +$default-color-rhino-light-3: #f3f6fa; // Page Background +$default-color-rhino-light-4: #fafbfd; // Modal Tabbed Content Background + +// --- +// Primary +// -- +$default-color-dodger: #119DFF; // Accent +$default-color-dodger-shade: #0D76BF; // Link Hover + +// --- +// Accent +// --- +$default-color-aqua: #09ffff; +$default-color-aqua-shade: #19d3f3; // Body Text +$default-color-lavender: #e763fa; +$default-color-lavender-shade: #ab63fa; +$default-color-cornflower: #636efa; +$default-color-emerald: #00cc96; // CTA's on blue backgrounds +$default-color-sienna: #ef553b; // CTA's on blue backgrounds + +// --- +// Buttons +// --- +$default-color-button-default: #119dff; +$default-color-button-hover: #0f89df; +$default-color-button-active: #0d76bf; diff --git a/src/styles/variables/_main.scss b/src/styles/variables/_main.scss new file mode 100644 index 000000000..d62a1ea89 --- /dev/null +++ b/src/styles/variables/_main.scss @@ -0,0 +1,51 @@ +@import 'defaults'; + + +$font-weight-light: $default-font-weight-light; +$font-weight-normal: $default-font-weight-normal; +$font-weight-semibold: $default-font-weight-semibold; +$font-size: $default-font-size; +$font-size-small: $default-font-size-small; +$font-size-medium: $default-font-size-medium; +$h5-size: $default-h5-size; + +/* + * SPACING + */ +$base-spacing-unit: $default-base-spacing-unit; +$half-spacing-unit: $default-half-spacing-unit; +$quarter-spacing-unit: $default-quarter-spacing-unit; +$sixth-spacing-unit: $default-sixth-spacing-unit; +$eighth-spacing-unit: $default-eighth-spacing-unit; + +/* + * BORDERS + */ +$border-radius: $default-border-radius; +$border-radius-5: $default-border-radius-5; + +/* + * COLORS + */ + +// base +$color-brand: $default-color-navyblue; +$color-white: $default-color-white; +$color-highlight-darker: $default-color-gray-blue-pale; + +// text +$color-active-text: $default-color-rhino-core; +$color-text: $default-color-rhino-dark; +$color-secondary-text: $default-color-rhino-medium-1; +$color-header-text: $default-color-rhino-core; +$color-link-light: $default-color-blue-pale; + +// backgrounds +$color-panel-background: $default-color-white; +$color-section-header-background: $default-color-white-blue; + +// themes +$color-border: $default-color-rhino-medium-2; +$color-border-secondary: $default-color-rhino-light-1; +$color-button-secondary: $default-color-rhino-light-2; +$color-page-background: $default-color-rhino-light-3;