From bf4376e0a0e5f6a2165dfbfd57ba3f8e35cd56f9 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 22:30:50 -0500 Subject: [PATCH 01/27] add back eslint to dev tooling but integrate with prettier --- .eslintrc | 121 ++++++ package-lock.json | 1059 +++++++++++++++++++++++++++++++++++++++++++-- package.json | 8 +- 3 files changed, 1149 insertions(+), 39 deletions(-) create mode 100644 .eslintrc 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", From 19ef73ec398d776d7c9a81c5a31fd6136aab988b Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 22:31:34 -0500 Subject: [PATCH 02/27] lint Section --- src/components/containers/Section.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/containers/Section.js b/src/components/containers/Section.js index 5b681598a..5df8dcb8d 100644 --- a/src/components/containers/Section.js +++ b/src/components/containers/Section.js @@ -1,7 +1,6 @@ import SubPanel from './SubPanel'; import React, {Component, cloneElement} from 'react'; import PropTypes from 'prop-types'; -import {icon} from '../../lib'; import unpackPlotProps from '../../lib/unpackPlotProps'; function childIsVisible(child) { @@ -32,7 +31,7 @@ class Section extends Component { let subPanel = null; for (let i = 0; i < children.length; i++) { - let child = children[i]; + const child = children[i]; if (!child) { continue; } @@ -45,11 +44,11 @@ class Section extends Component { continue; } - let isAttr = !!child.props.attr; - let plotProps = isAttr + const isAttr = Boolean(child.props.attr); + const plotProps = isAttr ? unpackPlotProps(child.props, context, child.constructor) : {isVisible: true}; - let childProps = Object.assign({plotProps}, child.props); + const childProps = Object.assign({plotProps}, child.props); childProps.key = i; attrChildren.push(cloneElement(child, childProps)); } @@ -75,8 +74,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, From f39568e408b5a0f42e5b7f2b8afd977be8dceeea Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 22:31:49 -0500 Subject: [PATCH 03/27] lint DataSelector --- src/components/fields/DataSelector.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/fields/DataSelector.js b/src/components/fields/DataSelector.js index d5ce33f7b..1f31b244b 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); } From 529fa6034e482cd2c5eb66a2718b110ee91c3633 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 22:32:09 -0500 Subject: [PATCH 04/27] lint Dropdown and Radio --- src/components/fields/Dropdown.js | 2 +- src/components/fields/Radio.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/fields/Dropdown.js b/src/components/fields/Dropdown.js index 8a0e679f2..a2b6a571a 100644 --- a/src/components/fields/Dropdown.js +++ b/src/components/fields/Dropdown.js @@ -2,7 +2,7 @@ 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() { 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() { From 953598f4e238c614ae6b013038f9d44237152c0c Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 22:32:44 -0500 Subject: [PATCH 05/27] css fix (lowercase) --- src/styles/components/fields/_main.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/components/fields/_main.scss b/src/styles/components/fields/_main.scss index c55ebee7d..c24d775dc 100644 --- a/src/styles/components/fields/_main.scss +++ b/src/styles/components/fields/_main.scss @@ -1,5 +1,5 @@ .field { - Align-items: center; + align-items: center; border-top: 1px solid #f8f8f9; color: #6D6D6D; display: flex; From 02f7be2af7a7cd250ab0b80dfa47fffb2a618036 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 22:35:36 -0500 Subject: [PATCH 06/27] introduce connectAxesToLayout higher order component This requires plotly.Axes methods so we shamefully hack those on for now --- src/PlotlyEditor.js | 3 + src/lib/connectAxesToLayout.js | 232 +++++++++++++++++++++++++++++++++ src/lib/walkObject.js | 2 +- src/shame.js | 12 ++ 4 files changed, 248 insertions(+), 1 deletion(-) create mode 100644 src/lib/connectAxesToLayout.js create mode 100644 src/shame.js diff --git a/src/PlotlyEditor.js b/src/PlotlyEditor.js index 94e7b7a38..375cbb868 100644 --- a/src/PlotlyEditor.js +++ b/src/PlotlyEditor.js @@ -3,11 +3,14 @@ import PropTypes from 'prop-types'; import React, {Component} from 'react'; import dictionaries from './locales'; import {bem} from './lib'; +import {noShame} from './shame'; class PlotlyEditor extends Component { constructor(props, context) { super(props, context); + noShame({plotly: this.props.plotly}); + // we only need to compute this once. this.plotSchema = this.props.plotly.PlotSchema.get(); } diff --git a/src/lib/connectAxesToLayout.js b/src/lib/connectAxesToLayout.js new file mode 100644 index 000000000..27e38ba9c --- /dev/null +++ b/src/lib/connectAxesToLayout.js @@ -0,0 +1,232 @@ +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, searchArrays) { + var intoVal = intoObj[key], + fromVal = fromObj[key]; + + // 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; + this.axes = plotly.Axes.list(graphDiv); + 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) + ) + ); + 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.isRequired, + updateContainer: PropTypes.func.isRequired, + }; + + 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/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, + }; + } +} From c89dc4bd3413137191c208712358df12359c538e Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 22:36:21 -0500 Subject: [PATCH 07/27] introduce Axes Selector --- src/components/fields/AxesSelector.js | 52 +++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/components/fields/AxesSelector.js 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, +}; From a6f14c55b6e66f2cb6d1bf5fb7688012c7b28bd9 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 22:36:57 -0500 Subject: [PATCH 08/27] thread new items though index.js files --- src/components/fields/index.js | 2 ++ src/components/index.js | 2 ++ src/index.js | 4 ++++ src/lib/index.js | 6 ++++-- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/fields/index.js b/src/components/fields/index.js index a18b3f12f..2e6660b15 100644 --- a/src/components/fields/index.js +++ b/src/components/fields/index.js @@ -1,3 +1,4 @@ +import AxesSelector from './AxesSelector'; import ColorPicker from './Color'; import Dropdown from './Dropdown'; import Flaglist from './Flaglist'; @@ -8,6 +9,7 @@ import Numeric from './Numeric'; import TraceSelector from './TraceSelector'; export { + AxesSelector, ColorPicker, Dropdown, Flaglist, diff --git a/src/components/index.js b/src/components/index.js index 4bd7f548c..c88ebf9f8 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,4 +1,5 @@ import { + AxesSelector, ColorPicker, Dropdown, Flaglist, @@ -13,6 +14,7 @@ import {SubPanel, Fold, Panel, Section, TraceAccordion} from './containers'; import PanelMenuWrapper from './PanelMenuWrapper'; export { + AxesSelector, SubPanel, ColorPicker, DataSelector, diff --git a/src/index.js b/src/index.js index 803873da8..2571feab6 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,6 +10,7 @@ import { import {EDITOR_ACTIONS} from './constants'; import { + AxesSelector, SubPanel, ColorPicker, DataSelector, @@ -27,6 +29,7 @@ import { } from './components'; export { + AxesSelector, SubPanel, ColorPicker, DataSelector, @@ -44,6 +47,7 @@ export { Section, TraceAccordion, TraceSelector, + connectAxesToLayout, connectLayoutToPlot, connectToContainer, connectTraceToPlot, 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, }; From ee1c27c4a00280bff5ddeca803b304b952db2543 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 22:38:11 -0500 Subject: [PATCH 09/27] Rewrite Numeric input to use new multivalued subsystem --- src/components/fields/Numeric.js | 6 +- src/components/widgets/NumericInput.js | 114 --------- .../widgets/NumericInputStatefulWrapper.js | 242 +++++++----------- src/lib/connectToContainer.js | 5 +- src/lib/constants.js | 4 +- src/lib/unpackPlotProps.js | 39 ++- 6 files changed, 136 insertions(+), 274 deletions(-) delete mode 100644 src/components/widgets/NumericInput.js diff --git a/src/components/fields/Numeric.js b/src/components/fields/Numeric.js index fa1222368..5ff0393db 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,11 +10,13 @@ class Numeric extends Component { @@ -23,11 +25,13 @@ class Numeric extends Component { } Numeric.propTypes = { + defaultValue: PropTypes.number, fullValue: PropTypes.func, min: PropTypes.number, max: PropTypes.number, step: PropTypes.number, updatePlot: PropTypes.func, + multiValued: PropTypes.bool, ...Field.propTypes, }; 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..5b9e521a1 100644 --- a/src/components/widgets/NumericInputStatefulWrapper.js +++ b/src/components/widgets/NumericInputStatefulWrapper.js @@ -1,191 +1,147 @@ -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 (this.props.multiValued) { + // 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,*/ + multiValued: PropTypes.bool }; NumericInputStatefulWrapper.defaultProps = { diff --git a/src/lib/connectToContainer.js b/src/lib/connectToContainer.js index 6209b71a5..9a4ef105c 100644 --- a/src/lib/connectToContainer.js +++ b/src/lib/connectToContainer.js @@ -39,9 +39,9 @@ export default function connectToContainer(WrappedComponent) { ); if (props.isVisible) { return ; - } else { - return null; } + + return null; } } @@ -51,6 +51,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/constants.js b/src/lib/constants.js index 76226479f..e4b310eab 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -8,10 +8,10 @@ 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 MULTI_VALUED_PLACEHOLDER = "-"; /* export const CLEAR_WORKSPACE = "WORKSPACE_CLEAR_WORKSPACE"; diff --git a/src/lib/unpackPlotProps.js b/src/lib/unpackPlotProps.js index 197a321c3..37feb16e7 100644 --- a/src/lib/unpackPlotProps.js +++ b/src/lib/unpackPlotProps.js @@ -1,21 +1,31 @@ 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, 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) || {}; @@ -23,21 +33,25 @@ export default function unpackPlotProps(props, context, ComponentClass) { // 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, fullContainer, fullValue, isVisible, updateContainer, updatePlot, + multiValued, }; if (isNumeric(attrMeta.max)) { @@ -47,9 +61,10 @@ export default function unpackPlotProps(props, context, ComponentClass) { 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; } From f237701e43e5ceccc4d3d7fbf0ba0b80a5f7b2b2 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 22:39:26 -0500 Subject: [PATCH 10/27] sketch out DefaultEditor Axes panel --- src/DefaultEditor.js | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/DefaultEditor.js b/src/DefaultEditor.js index df421feab..7fde47b92 100644 --- a/src/DefaultEditor.js +++ b/src/DefaultEditor.js @@ -1,7 +1,7 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import { - SubPanel, + AxesSelector, ColorPicker, DataSelector, Dropdown, @@ -13,13 +13,15 @@ import { PanelMenuWrapper, Radio, Section, + SubPanel, TraceAccordion, 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) { @@ -27,7 +29,7 @@ class DefaultEditor extends Component { const capitalize = s => s.charAt(0).toUpperCase() + s.substring(1); - // Filter out Polar "area" type (it is fairly broken and we want to present + // 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(context.plotSchema.traces).filter( t => t !== 'area' @@ -180,6 +182,38 @@ class DefaultEditor extends Component { + + + + + + + + + + + + + + + + + + + + + + + + + + +
From 58b6904fa922caefe8484dc515d679bb8f97704e Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 23:13:38 -0500 Subject: [PATCH 11/27] Widgets no longer need know about multivalued at all --- src/components/fields/Numeric.js | 2 -- src/components/widgets/NumericInputStatefulWrapper.js | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/fields/Numeric.js b/src/components/fields/Numeric.js index 5ff0393db..a886b6af6 100644 --- a/src/components/fields/Numeric.js +++ b/src/components/fields/Numeric.js @@ -16,7 +16,6 @@ class Numeric extends Component { max={this.props.max} onChange={this.props.updatePlot} onUpdate={this.props.updatePlot} - multiValued={this.props.multiValued} showArrows /> @@ -31,7 +30,6 @@ Numeric.propTypes = { max: PropTypes.number, step: PropTypes.number, updatePlot: PropTypes.func, - multiValued: PropTypes.bool, ...Field.propTypes, }; diff --git a/src/components/widgets/NumericInputStatefulWrapper.js b/src/components/widgets/NumericInputStatefulWrapper.js index 5b9e521a1..7d7f12e0d 100644 --- a/src/components/widgets/NumericInputStatefulWrapper.js +++ b/src/components/widgets/NumericInputStatefulWrapper.js @@ -76,7 +76,7 @@ export default class NumericInputStatefulWrapper extends Component { } else { valueUpdate = value - step; } - } else if (this.props.multiValued) { + } 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)) { @@ -141,7 +141,6 @@ NumericInputStatefulWrapper.propTypes = { showArrows: PropTypes.bool, step: PropTypes.number, value: PropTypes.any, - multiValued: PropTypes.bool }; NumericInputStatefulWrapper.defaultProps = { From 8ff079e429fec812354939167891029db0545185 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Mon, 13 Nov 2017 23:14:07 -0500 Subject: [PATCH 12/27] Dropdown handles multivalued by changing placeholder --- src/components/fields/Dropdown.js | 6 ++++++ src/lib/constants.js | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/fields/Dropdown.js b/src/components/fields/Dropdown.js index a2b6a571a..58a386b9c 100644 --- a/src/components/fields/Dropdown.js +++ b/src/components/fields/Dropdown.js @@ -6,6 +6,11 @@ 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/lib/constants.js b/src/lib/constants.js index e4b310eab..6a5bb83ea 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -11,7 +11,7 @@ export const baseClass = "plotly-editor"; export const MULTI_VALUED = "\x1bMIXED_VALUES"; // how mixed values are represented in text inputs -export const MULTI_VALUED_PLACEHOLDER = "-"; +export const MULTI_VALUED_PLACEHOLDER = "---"; /* export const CLEAR_WORKSPACE = "WORKSPACE_CLEAR_WORKSPACE"; From 60fccf0fa18aa175db7b560621f4e8ef2c2a2d0f Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Tue, 14 Nov 2017 19:03:32 -0500 Subject: [PATCH 13/27] replace all non-brand scss with brand scss. Create Info scss --- src/styles/_helpers.scss | 2 +- src/styles/_variables.scss | 70 +++++++++++++++++-- src/styles/components/containers/_info.scss | 23 ++++++ src/styles/components/containers/_main.scss | 2 + .../components/containers/_menupanel.scss | 49 +++++++++++++ src/styles/components/containers/_panel.scss | 36 +--------- .../components/containers/_section.scss | 1 + src/styles/components/fields/_main.scss | 2 +- .../components/widgets/_colorpicker.scss | 6 +- src/styles/icons/_icons.scss | 5 +- 10 files changed, 149 insertions(+), 47 deletions(-) create mode 100644 src/styles/components/containers/_info.scss create mode 100644 src/styles/components/containers/_menupanel.scss diff --git a/src/styles/_helpers.scss b/src/styles/_helpers.scss index 749b9cdce..99c9d7695 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-gray-dark; } diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index ef82f1079..cebe2621e 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -2,8 +2,10 @@ * SIZING AND WEIGHTS */ $font-weight-light: 300; +$font-weight-normal: 400; $font-weight-semibold: 600; $font-size-small: 12px; +$font-size-medium: 14px; $h5-size: 16px !default; /* @@ -24,14 +26,70 @@ $border-radius-5: 5px; /* * COLORS */ +$color-black-dark: #000; +$color-black-paleish: #333; +$color-black-pale: #666; +$color-black: #111; +$color-navyblue: #284576; +$color-blue: #119DFF; +$color-blue-dark: darken($color-blue, 20%); +$color-blue-pale: #45a9ff; +$color-blue-paleish: #2391fe; +$color-cyan-pale: #9bd1ff; +$color-gold: #f7bd0c; +$color-gray-blue: #69738a; +$color-gray-blue-pale: #bec8d9; +$color-gray-dark: #888; +$color-gray-light: #ddd; +$color-gray: #ccc; +$color-green-cyan: #53cf9d; +$color-green-pale: #dff0d8; +$color-green: #3c763d; +$color-red-pale: #f2dede; +$color-red: #a94442; +$color-white-blue: #f4faff; +$color-white-dark: #eee; +$color-white-darkish: #f8f8f8; $color-white: #ffffff; -$lightgrey: #dcdddd; -$color-muted-text: #777; -$dark-grey: #4c5059; -$color-page-background: #f3f6fa; -$color-highlight-darker: #bec8d9; + +// --- +// Text and Backgrounds +// --- +$color-rhino-core: #2a3f5f; // Active state text, emphasized text +$color-rhino-dark: #506784; // Default text color +$color-rhino-medium-1: #a2b1c6; +$color-rhino-medium-2: #c8d4e3; // Border Default +$color-rhino-light-1: #dfe8f3; // Border Secondary +$color-rhino-light-2: #ebf0f8; // Button Secondary +$color-rhino-light-3: #f3f6fa; // Page Background +$color-rhino-light-4: #fafbfd; // Modal Tabbed Content Background + + +// --- +// Primary +// -- +$color-dodger: #119DFF; // Accent +$color-dodger-shade: #0D76BF; // Link Hover + +// --- +// Accent +// --- +$color-aqua: #09ffff; +$color-aqua-shade: #19d3f3; // Body Text +$color-lavender: #e763fa; +$color-lavender-shade: #ab63fa; +$color-cornflower: #636efa; +$color-emerald: #00cc96; // CTA's on blue backgrounds +$color-sienna: #ef553b; // CTA's on blue backgrounds + +// --- +// Buttons +// --- +$color-button-default: #119dff; +$color-button-hover: #0f89df; +$color-button-active: #0d76bf; /* * COMPONENT */ -$fold-border: 1px solid $color-highlight-darker; +$fold-border: 1px solid $color-gray-blue-pale; diff --git a/src/styles/components/containers/_info.scss b/src/styles/components/containers/_info.scss new file mode 100644 index 000000000..49b4070a2 --- /dev/null +++ b/src/styles/components/containers/_info.scss @@ -0,0 +1,23 @@ +.info__title { + color: $color-blue-pale; + 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-rhino-dark; + font-size: $font-size-small; + font-weight: $font-weight-light; + line-height: 18px; +} + +.info__sub-text { + color: $color-navyblue; + 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..7072badaa 100644 --- a/src/styles/components/containers/_main.scss +++ b/src/styles/components/containers/_main.scss @@ -1,3 +1,5 @@ @import "panel"; @import "fold"; @import "section"; +@import "menupanel"; +@import "info"; diff --git a/src/styles/components/containers/_menupanel.scss b/src/styles/components/containers/_menupanel.scss new file mode 100644 index 000000000..eb050858e --- /dev/null +++ b/src/styles/components/containers/_menupanel.scss @@ -0,0 +1,49 @@ +.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"); +} + +.subpanel__container { + padding-top: 0; +} + +.subpanel__container--ownline { + padding-top: 6px; +} + +.subpanel__icon-span { + font-size: $font-size-small; +} + +.subpanel__icon-span--question { + color: $color-blue-pale; +} + +.subpanel__icon { + padding-left: 6px; + padding-right: 6px; + vertical-align: middle; +} diff --git a/src/styles/components/containers/_panel.scss b/src/styles/components/containers/_panel.scss index f707b35ce..803289860 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-rhino-light-3; 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..52351ae03 100644 --- a/src/styles/components/containers/_section.scss +++ b/src/styles/components/containers/_section.scss @@ -1,4 +1,5 @@ .section__heading { + display: flex; font-size: 13px; color: #69738a; font-weight: 400; diff --git a/src/styles/components/fields/_main.scss b/src/styles/components/fields/_main.scss index c24d775dc..2e049daac 100644 --- a/src/styles/components/fields/_main.scss +++ b/src/styles/components/fields/_main.scss @@ -4,7 +4,7 @@ color: #6D6D6D; display: flex; font-size: 13px; - font-weight: 300; + font-weight: $font-weight-light; justify-content: flex-start; line-height: 13px; min-height: 32px; diff --git a/src/styles/components/widgets/_colorpicker.scss b/src/styles/components/widgets/_colorpicker.scss index 0a9244c43..561050d11 100644 --- a/src/styles/components/widgets/_colorpicker.scss +++ b/src/styles/components/widgets/_colorpicker.scss @@ -43,7 +43,7 @@ $slider-picker-height: 10px; width: 32px; height: 32px; border-radius: 50%; - border: 1px solid $lightgrey; + border: 1px solid $color-gray-light; vertical-align: middle; padding-top: $sixth-spacing-unit; padding-left: $sixth-spacing-unit; @@ -52,7 +52,7 @@ $slider-picker-height: 10px; .colorpicker__selected-color { margin-left: $half-spacing-unit; - color: $color-muted-text; + color: $color-black-pale; font-weight: $font-weight-light; font-size: $font-size-small; display: inline-block; @@ -68,7 +68,7 @@ $slider-picker-height: 10px; } .toolLabel { - color: $color-muted-text; + color: $color-black-pale; font-size: $font-size-small; font-weight: $font-weight-light; } diff --git a/src/styles/icons/_icons.scss b/src/styles/icons/_icons.scss index 4d5405727..c369663db 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,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') format("truetype"); + src: url('data:font/truetype;charset=utf-8;base64,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') format("truetype"); font-weight: normal; font-style: normal; } @@ -34,3 +34,6 @@ .plotlyjs_editor__icon-cog:before { content: "\61"; } +.plotlyjs_editor__icon-question-circle:before { + content: "\62"; +} From 8f24a74c8b56806e0dd8c93f8e72250883bba1c0 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Tue, 14 Nov 2017 19:07:04 -0500 Subject: [PATCH 14/27] PanelMenuWrapper does not need to be localized --- src/components/PanelMenuWrapper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/PanelMenuWrapper.js b/src/components/PanelMenuWrapper.js index 8d51c707b..b5dc10430 100644 --- a/src/components/PanelMenuWrapper.js +++ b/src/components/PanelMenuWrapper.js @@ -1,6 +1,6 @@ import SidebarGroup from './sidebar/SidebarGroup'; import React, {cloneElement, Component} from 'react'; -import {bem, localize} from '../lib'; +import {bem} from '../lib'; class PanelsWithSidebar extends Component { constructor(props) { @@ -87,4 +87,4 @@ class PanelsWithSidebar extends Component { } } -export default localize(PanelsWithSidebar); +export default PanelsWithSidebar; From 4949d64018044b4078cc5854a656981f0ce3b088 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Tue, 14 Nov 2017 19:07:41 -0500 Subject: [PATCH 15/27] field and subpanel can be configured to handle multiValue info --- src/components/containers/SubPanel.js | 45 +++++++++++--- src/components/fields/Field.js | 89 ++++++++++++++++++--------- src/lib/constants.js | 16 ++++- 3 files changed, 107 insertions(+), 43 deletions(-) diff --git a/src/components/containers/SubPanel.js b/src/components/containers/SubPanel.js index e20e9a5fc..941065d0b 100644 --- a/src/components/containers/SubPanel.js +++ b/src/components/containers/SubPanel.js @@ -1,5 +1,6 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; +import classnames from 'classnames'; export default class SubPanel extends Component { constructor() { @@ -9,17 +10,41 @@ export default class SubPanel extends Component { this.toggleVisibility = this.toggleVisibility.bind(this); } + subpanelClasses() { + if (this.props.iconClass) { + return { + iconClass: `subpanel__icon ${this.props.iconClass}`, + spanClass: 'subpanel__icon-span', + }; + } else if (this.props.question) { + return { + iconClass: 'subpanel__icon plotlyjs_editor__icon-question-circle', + spanClass: `subpanel__icon-span subpanel__icon-span--question`, + }; + } + return { + iconClass: 'subpanel__icon plotlyjs_editor__icon-cog', + spanClass: 'subpanel__icon-span subpanel__icon-span--cog', + }; + } + toggleVisibility() { this.setState({isVisible: !this.state.isVisible}); } render() { - const toggleClass = `subpanel__toggle ${this.props.toggleIconClass}`; const isVisible = this.props.show || this.state.isVisible; + const containerClass = classnames('subpanel__container', { + 'subpanel__container--ownline': this.props.ownline, + }); + + const {iconClass, spanClass} = this.subpanelClasses(); + return ( - - - +
+ + {this.props.label} + {isVisible ? (
@@ -27,16 +52,16 @@ export default class SubPanel extends Component {
{this.props.children}
) : null} - +
); } } SubPanel.propTypes = { - toggleIconClass: PropTypes.string.isRequired, + children: PropTypes.node, + iconClass: PropTypes.string, show: PropTypes.bool, -}; - -SubPanel.defaultProps = { - toggleIconClass: 'plotlyjs_editor__icon-cog', + ownline: PropTypes.bool, + question: PropTypes.bool, + label: PropTypes.string, }; diff --git a/src/components/fields/Field.js b/src/components/fields/Field.js index 4a5c2bdee..617b45b3a 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 SubPanel from '../containers/SubPanel'; +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/lib/constants.js b/src/lib/constants.js index 6a5bb83ea..807b86b90 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,10 +8,20 @@ export const baseClass = "plotly-editor"; * strings, we include a non-printable character (ESC) so it's not something * people could type. */ -export const MULTI_VALUED = "\x1bMIXED_VALUES"; +export const MULTI_VALUED = '\x1bMIXED_VALUES'; // how mixed values are represented in text inputs -export const MULTI_VALUED_PLACEHOLDER = "---"; +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 CLEAR_WORKSPACE = "WORKSPACE_CLEAR_WORKSPACE"; From 380cc35111013ebb1b277c2517b918ec24896a8b Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Tue, 14 Nov 2017 19:08:26 -0500 Subject: [PATCH 16/27] Info does not use propTypes --- src/components/fields/Info.js | 1 - 1 file changed, 1 deletion(-) 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 { From e4c204918da87e74b4b4126a45f1045c32fab95c Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Wed, 15 Nov 2017 00:11:52 -0500 Subject: [PATCH 17/27] Add AxesRange component to handle special visibility When autorange=true range components are not visible --- src/DefaultEditor.js | 21 ++++++++++++++++++++ src/components/containers/Section.js | 2 +- src/components/fields/AxesRange.js | 29 ++++++++++++++++++++++++++++ src/components/fields/Numeric.js | 7 ++++++- src/components/fields/index.js | 2 ++ src/components/index.js | 2 ++ src/index.js | 2 ++ src/lib/connectToContainer.js | 6 ++++++ 8 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 src/components/fields/AxesRange.js diff --git a/src/DefaultEditor.js b/src/DefaultEditor.js index 7fde47b92..eb65e1330 100644 --- a/src/DefaultEditor.js +++ b/src/DefaultEditor.js @@ -2,6 +2,7 @@ import React, {Component} from 'react'; import PropTypes from 'prop-types'; import { AxesSelector, + AxesRange, ColorPicker, DataSelector, Dropdown, @@ -194,9 +195,29 @@ class DefaultEditor extends Component { + +
+ + + + +
+ diff --git a/src/components/containers/Section.js b/src/components/containers/Section.js index 5df8dcb8d..7ba68da10 100644 --- a/src/components/containers/Section.js +++ b/src/components/containers/Section.js @@ -46,7 +46,7 @@ class Section extends Component { const isAttr = Boolean(child.props.attr); const plotProps = isAttr - ? unpackPlotProps(child.props, context, child.constructor) + ? unpackPlotProps(child.props, context, child.type) : {isVisible: true}; const childProps = Object.assign({plotProps}, child.props); childProps.key = i; 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/Numeric.js b/src/components/fields/Numeric.js index a886b6af6..9632515d1 100644 --- a/src/components/fields/Numeric.js +++ b/src/components/fields/Numeric.js @@ -16,7 +16,7 @@ class Numeric extends Component { max={this.props.max} onChange={this.props.updatePlot} onUpdate={this.props.updatePlot} - showArrows + showArrows={this.props.showArrows} /> ); @@ -28,9 +28,14 @@ Numeric.propTypes = { fullValue: PropTypes.func, min: PropTypes.number, max: PropTypes.number, + showArrows: PropTypes.number, step: PropTypes.number, updatePlot: PropTypes.func, ...Field.propTypes, }; +Numeric.defaultProps = { + showArrows: true, +}; + export default connectToContainer(Numeric); diff --git a/src/components/fields/index.js b/src/components/fields/index.js index 2e6660b15..3a5dcfeff 100644 --- a/src/components/fields/index.js +++ b/src/components/fields/index.js @@ -1,3 +1,4 @@ +import AxesRange from './AxesRange'; import AxesSelector from './AxesSelector'; import ColorPicker from './Color'; import Dropdown from './Dropdown'; @@ -9,6 +10,7 @@ import Numeric from './Numeric'; import TraceSelector from './TraceSelector'; export { + AxesRange, AxesSelector, ColorPicker, Dropdown, diff --git a/src/components/index.js b/src/components/index.js index c88ebf9f8..d4d22f525 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,4 +1,5 @@ import { + AxesRange, AxesSelector, ColorPicker, Dropdown, @@ -15,6 +16,7 @@ import PanelMenuWrapper from './PanelMenuWrapper'; export { AxesSelector, + AxesRange, SubPanel, ColorPicker, DataSelector, diff --git a/src/index.js b/src/index.js index 2571feab6..4bf8453ba 100644 --- a/src/index.js +++ b/src/index.js @@ -10,6 +10,7 @@ import { import {EDITOR_ACTIONS} from './constants'; import { + AxesRange, AxesSelector, SubPanel, ColorPicker, @@ -29,6 +30,7 @@ import { } from './components'; export { + AxesRange, AxesSelector, SubPanel, ColorPicker, diff --git a/src/lib/connectToContainer.js b/src/lib/connectToContainer.js index 9a4ef105c..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); From f51c8bb8064cd887426369c6e37e9bf841de6e99 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Wed, 15 Nov 2017 10:14:56 -0500 Subject: [PATCH 18/27] fix for TraceSelector TraceSelector was not updated when we moved to the container system. Tests! --- src/components/fields/TraceSelector.js | 32 +++++++++++++++----------- src/lib/unpackPlotProps.js | 9 +++++++- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/components/fields/TraceSelector.js b/src/components/fields/TraceSelector.js index 2032d6901..3815b8e55 100644 --- a/src/components/fields/TraceSelector.js +++ b/src/components/fields/TraceSelector.js @@ -5,13 +5,13 @@ import nestedProperty from 'plotly.js/src/lib/nested_property'; import {connectToContainer} from '../../lib'; class TraceSelector extends Component { - constructor(props, context) { - super(props, context); + constructor(props) { + super(props); 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'); + const fillMeta = props.getValObject('fill'); + this.fillTypes = fillMeta.values.filter(v => v !== 'none'); } updatePlot(value) { @@ -26,18 +26,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'; @@ -60,8 +61,11 @@ class TraceSelector extends Component { } } -TraceSelector.contextTypes = { - plotSchema: PropTypes.object, +TraceSelector.propTypes = { + getValObject: PropTypes.func.isRequired, + container: PropTypes.object.isRequired, + fullValue: PropTypes.func.isRequired, + updateContainer: PropTypes.func, }; export default connectToContainer(TraceSelector); diff --git a/src/lib/unpackPlotProps.js b/src/lib/unpackPlotProps.js index 37feb16e7..46f6190cd 100644 --- a/src/lib/unpackPlotProps.js +++ b/src/lib/unpackPlotProps.js @@ -3,7 +3,13 @@ import isNumeric from 'fast-isnumeric'; import {MULTI_VALUED, MULTI_VALUED_PLACEHOLDER} from './constants'; export default function unpackPlotProps(props, context, ComponentClass) { - const {container, defaultContainer, fullContainer, updateContainer} = context; + const { + container, + getValObject, + defaultContainer, + fullContainer, + updateContainer, + } = context; if (!container || !fullContainer) { throw new Error( @@ -46,6 +52,7 @@ export default function unpackPlotProps(props, context, ComponentClass) { attrMeta, container, defaultValue, + getValObject, fullContainer, fullValue, isVisible, From fa5dcc5d290baa081b8bcafc028b30641dd924a4 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Wed, 15 Nov 2017 12:53:44 -0500 Subject: [PATCH 19/27] create a more reasonable scss system A variables main file defines the customizable variables in the editor. This is only partially complete. Better standardization and coverage is required. Use a default vars file to assign plotly defaults to the customizable variables. --- src/styles/_helpers.scss | 2 +- src/styles/_mixins.scss | 12 +++ src/styles/_variables.scss | 95 ------------------- src/styles/components/containers/_fold.scss | 4 +- src/styles/components/containers/_info.scss | 6 +- .../components/containers/_menupanel.scss | 4 +- src/styles/components/containers/_panel.scss | 2 +- .../components/containers/_section.scss | 6 +- src/styles/components/fields/_field.scss | 59 ++++++++++++ src/styles/components/fields/_main.scss | 61 +----------- .../components/widgets/_colorpicker.scss | 13 +-- .../components/widgets/_numeric-input.scss | 4 +- src/styles/icons/_icons.scss | 8 +- src/styles/main.scss | 2 +- src/styles/variables/_defaults.scss | 90 ++++++++++++++++++ src/styles/variables/_main.scss | 51 ++++++++++ 16 files changed, 239 insertions(+), 180 deletions(-) delete mode 100644 src/styles/_variables.scss create mode 100644 src/styles/components/fields/_field.scss create mode 100644 src/styles/variables/_defaults.scss create mode 100644 src/styles/variables/_main.scss diff --git a/src/styles/_helpers.scss b/src/styles/_helpers.scss index 99c9d7695..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: $color-gray-dark; } +.\+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 cebe2621e..000000000 --- a/src/styles/_variables.scss +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SIZING AND WEIGHTS - */ -$font-weight-light: 300; -$font-weight-normal: 400; -$font-weight-semibold: 600; -$font-size-small: 12px; -$font-size-medium: 14px; -$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-black-dark: #000; -$color-black-paleish: #333; -$color-black-pale: #666; -$color-black: #111; -$color-navyblue: #284576; -$color-blue: #119DFF; -$color-blue-dark: darken($color-blue, 20%); -$color-blue-pale: #45a9ff; -$color-blue-paleish: #2391fe; -$color-cyan-pale: #9bd1ff; -$color-gold: #f7bd0c; -$color-gray-blue: #69738a; -$color-gray-blue-pale: #bec8d9; -$color-gray-dark: #888; -$color-gray-light: #ddd; -$color-gray: #ccc; -$color-green-cyan: #53cf9d; -$color-green-pale: #dff0d8; -$color-green: #3c763d; -$color-red-pale: #f2dede; -$color-red: #a94442; -$color-white-blue: #f4faff; -$color-white-dark: #eee; -$color-white-darkish: #f8f8f8; -$color-white: #ffffff; - -// --- -// Text and Backgrounds -// --- -$color-rhino-core: #2a3f5f; // Active state text, emphasized text -$color-rhino-dark: #506784; // Default text color -$color-rhino-medium-1: #a2b1c6; -$color-rhino-medium-2: #c8d4e3; // Border Default -$color-rhino-light-1: #dfe8f3; // Border Secondary -$color-rhino-light-2: #ebf0f8; // Button Secondary -$color-rhino-light-3: #f3f6fa; // Page Background -$color-rhino-light-4: #fafbfd; // Modal Tabbed Content Background - - -// --- -// Primary -// -- -$color-dodger: #119DFF; // Accent -$color-dodger-shade: #0D76BF; // Link Hover - -// --- -// Accent -// --- -$color-aqua: #09ffff; -$color-aqua-shade: #19d3f3; // Body Text -$color-lavender: #e763fa; -$color-lavender-shade: #ab63fa; -$color-cornflower: #636efa; -$color-emerald: #00cc96; // CTA's on blue backgrounds -$color-sienna: #ef553b; // CTA's on blue backgrounds - -// --- -// Buttons -// --- -$color-button-default: #119dff; -$color-button-hover: #0f89df; -$color-button-active: #0d76bf; - -/* - * COMPONENT - */ -$fold-border: 1px solid $color-gray-blue-pale; 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 index 49b4070a2..812e6c2cf 100644 --- a/src/styles/components/containers/_info.scss +++ b/src/styles/components/containers/_info.scss @@ -1,5 +1,5 @@ .info__title { - color: $color-blue-pale; + color: $color-link-light; font-size: $font-size-medium; font-weight: $font-weight-normal; line-height: 18px; @@ -8,14 +8,14 @@ .info__text { padding: $quarter-spacing-unit $half-spacing-unit; - color: $color-rhino-dark; + color: $color-secondary-text; font-size: $font-size-small; font-weight: $font-weight-light; line-height: 18px; } .info__sub-text { - color: $color-navyblue; + color: $color-brand; font-size: $font-size-small; font-weight: $font-weight-light; line-height: 14px; diff --git a/src/styles/components/containers/_menupanel.scss b/src/styles/components/containers/_menupanel.scss index eb050858e..03e6251a6 100644 --- a/src/styles/components/containers/_menupanel.scss +++ b/src/styles/components/containers/_menupanel.scss @@ -3,7 +3,7 @@ border-radius: 2px; text-transform: none; text-align: left; - border: $fold-border; + border: 1px solid $color-border; align-content: center; box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.2); @@ -39,7 +39,7 @@ } .subpanel__icon-span--question { - color: $color-blue-pale; + color: $color-link-light; } .subpanel__icon { diff --git a/src/styles/components/containers/_panel.scss b/src/styles/components/containers/_panel.scss index 803289860..e1c87e408 100644 --- a/src/styles/components/containers/_panel.scss +++ b/src/styles/components/containers/_panel.scss @@ -5,7 +5,7 @@ bottom: 5px; right: 1px; margin-left: 100px; - background-color: $color-rhino-light-3; + background-color: $color-panel-background; overflow-y: scroll; padding-left: $half-spacing-unit; padding-right: $half-spacing-unit; diff --git a/src/styles/components/containers/_section.scss b/src/styles/components/containers/_section.scss index 52351ae03..d85c61b17 100644 --- a/src/styles/components/containers/_section.scss +++ b/src/styles/components/containers/_section.scss @@ -1,10 +1,10 @@ .section__heading { display: flex; - font-size: 13px; - color: #69738a; + 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..520945caa --- /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: 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; +} diff --git a/src/styles/components/fields/_main.scss b/src/styles/components/fields/_main.scss index 2e049daac..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: $font-weight-light; - 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/widgets/_colorpicker.scss b/src/styles/components/widgets/_colorpicker.scss index 561050d11..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 $color-gray-light; + 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-black-pale; + 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-black-pale; - 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..f43b6ab26 100644 --- a/src/styles/components/widgets/_numeric-input.scss +++ b/src/styles/components/widgets/_numeric-input.scss @@ -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 c369663db..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,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') format("truetype"); + src: url('data:font/truetype;charset=utf-8;base64,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') format("truetype"); font-weight: normal; font-style: normal; } @@ -37,3 +37,9 @@ .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; From 4b6f6525a6b4db5f70a33661d1c7c781aa212543 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Wed, 15 Nov 2017 17:07:52 -0500 Subject: [PATCH 20/27] rename SubPanel -> MenuPanel and split MenuPanel The underlying modal is useful to other dropdown like components so that has been split into something called ModalBox --- src/DefaultEditor.js | 7 +- src/components/containers/MenuPanel.js | 65 ++++++++++++++++++ src/components/containers/ModalBox.js | 24 +++++++ src/components/containers/Section.js | 20 +++--- src/components/containers/SubPanel.js | 67 ------------------- .../containers/__tests__/Section-test.js | 12 ++-- src/components/containers/index.js | 4 +- src/components/fields/Field.js | 6 +- src/components/index.js | 4 +- src/index.js | 4 +- src/styles/components/containers/_main.scss | 1 + .../components/containers/_menupanel.scss | 38 ++--------- .../components/containers/_modalbox.scss | 27 ++++++++ .../components/widgets/_numeric-input.scss | 2 +- 14 files changed, 152 insertions(+), 129 deletions(-) create mode 100644 src/components/containers/MenuPanel.js create mode 100644 src/components/containers/ModalBox.js delete mode 100644 src/components/containers/SubPanel.js create mode 100644 src/styles/components/containers/_modalbox.scss diff --git a/src/DefaultEditor.js b/src/DefaultEditor.js index eb65e1330..58ecd1c96 100644 --- a/src/DefaultEditor.js +++ b/src/DefaultEditor.js @@ -14,7 +14,8 @@ import { PanelMenuWrapper, Radio, Section, - SubPanel, + MenuPanel, + SymbolSelector, TraceAccordion, TraceSelector, } from './components'; @@ -278,7 +279,7 @@ class DefaultEditor extends Component { />
- +
{_( @@ -303,7 +304,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 7ba68da10..b5e8aaeee 100644 --- a/src/components/containers/Section.js +++ b/src/components/containers/Section.js @@ -1,4 +1,4 @@ -import SubPanel from './SubPanel'; +import MenuPanel from './MenuPanel'; import React, {Component, cloneElement} from 'react'; import PropTypes from 'prop-types'; import unpackPlotProps from '../../lib/unpackPlotProps'; @@ -12,7 +12,7 @@ class Section extends Component { super(props, context); this.children = null; - this.subPanel = null; + this.menuPanel = null; this.processAndSetChildren(context); } @@ -28,19 +28,19 @@ class Section extends Component { } const attrChildren = []; - let subPanel = null; + let menuPanel = null; for (let i = 0; i < children.length; 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; } @@ -54,19 +54,19 @@ class Section extends Component { } 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}
diff --git a/src/components/containers/SubPanel.js b/src/components/containers/SubPanel.js deleted file mode 100644 index 941065d0b..000000000 --- a/src/components/containers/SubPanel.js +++ /dev/null @@ -1,67 +0,0 @@ -import React, {Component} from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; - -export default class SubPanel extends Component { - constructor() { - super(); - this.state = {isVisible: false}; - - this.toggleVisibility = this.toggleVisibility.bind(this); - } - - subpanelClasses() { - if (this.props.iconClass) { - return { - iconClass: `subpanel__icon ${this.props.iconClass}`, - spanClass: 'subpanel__icon-span', - }; - } else if (this.props.question) { - return { - iconClass: 'subpanel__icon plotlyjs_editor__icon-question-circle', - spanClass: `subpanel__icon-span subpanel__icon-span--question`, - }; - } - return { - iconClass: 'subpanel__icon plotlyjs_editor__icon-cog', - spanClass: 'subpanel__icon-span subpanel__icon-span--cog', - }; - } - - toggleVisibility() { - this.setState({isVisible: !this.state.isVisible}); - } - - render() { - const isVisible = this.props.show || this.state.isVisible; - const containerClass = classnames('subpanel__container', { - 'subpanel__container--ownline': this.props.ownline, - }); - - const {iconClass, spanClass} = this.subpanelClasses(); - - return ( -
- - {this.props.label} - - - {isVisible ? ( -
-
-
{this.props.children}
-
- ) : null} -
- ); - } -} - -SubPanel.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/__tests__/Section-test.js b/src/components/containers/__tests__/Section-test.js index 702d9e9d9..6ab8826e3 100644 --- a/src/components/containers/__tests__/Section-test.js +++ b/src/components/containers/__tests__/Section-test.js @@ -1,6 +1,6 @@ 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 {connectTraceToPlot} from '../../../lib'; @@ -91,7 +91,7 @@ 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( { {...fixtures.scatter({deref: true})} >
- + INFO - - + + MISINFORMATION - +
).find('[name="test-section"]'); diff --git a/src/components/containers/index.js b/src/components/containers/index.js index af6ee56e2..73952bf73 100644 --- a/src/components/containers/index.js +++ b/src/components/containers/index.js @@ -1,7 +1,7 @@ -import SubPanel from './SubPanel'; +import MenuPanel from './MenuPanel'; import Fold from './Fold'; import Panel from './Panel'; import Section from './Section'; import TraceAccordion from './TraceAccordion'; -export {SubPanel, Fold, Panel, Section, TraceAccordion}; +export {MenuPanel, Fold, Panel, Section, TraceAccordion}; diff --git a/src/components/fields/Field.js b/src/components/fields/Field.js index 617b45b3a..cd0c795bc 100644 --- a/src/components/fields/Field.js +++ b/src/components/fields/Field.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React, {Component} from 'react'; -import SubPanel from '../containers/SubPanel'; +import MenuPanel from '../containers/MenuPanel'; import classnames from 'classnames'; import {bem, localize} from '../../lib'; import {multiValueText} from '../../lib/constants'; @@ -48,11 +48,11 @@ class Field extends Component {
{children} {multiValued ? ( - +
{_(multiValueText.title)}
{_(multiValueText.text)}
{_(multiValueText.subText)}
-
+ ) : null}
{postfix ? ( diff --git a/src/components/index.js b/src/components/index.js index d4d22f525..8b986c9da 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -11,13 +11,13 @@ import { TraceSelector, } from './fields'; -import {SubPanel, Fold, Panel, Section, TraceAccordion} from './containers'; +import {MenuPanel, Fold, Panel, Section, TraceAccordion} from './containers'; import PanelMenuWrapper from './PanelMenuWrapper'; export { AxesSelector, AxesRange, - SubPanel, + MenuPanel, ColorPicker, DataSelector, Dropdown, diff --git a/src/index.js b/src/index.js index 4bf8453ba..6fd85f805 100644 --- a/src/index.js +++ b/src/index.js @@ -12,7 +12,6 @@ import {EDITOR_ACTIONS} from './constants'; import { AxesRange, AxesSelector, - SubPanel, ColorPicker, DataSelector, Dropdown, @@ -25,6 +24,7 @@ import { PanelMenuWrapper, Radio, Section, + MenuPanel, TraceAccordion, TraceSelector, } from './components'; @@ -32,7 +32,7 @@ import { export { AxesRange, AxesSelector, - SubPanel, + MenuPanel, ColorPicker, DataSelector, Dropdown, diff --git a/src/styles/components/containers/_main.scss b/src/styles/components/containers/_main.scss index 7072badaa..93140140e 100644 --- a/src/styles/components/containers/_main.scss +++ b/src/styles/components/containers/_main.scss @@ -3,3 +3,4 @@ @import "section"; @import "menupanel"; @import "info"; +@import "modalbox"; diff --git a/src/styles/components/containers/_menupanel.scss b/src/styles/components/containers/_menupanel.scss index 03e6251a6..34992627b 100644 --- a/src/styles/components/containers/_menupanel.scss +++ b/src/styles/components/containers/_menupanel.scss @@ -1,48 +1,20 @@ -.subpanel { - 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"); -} - -.subpanel__cover { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - @include z-index("underground"); -} - -.subpanel__container { +.menupanel { padding-top: 0; } -.subpanel__container--ownline { +.menupanel--ownline { padding-top: 6px; } -.subpanel__icon-span { +.menupanel__icon-span { font-size: $font-size-small; } -.subpanel__icon-span--question { +.menupanel__icon-span--question { color: $color-link-light; } -.subpanel__icon { +.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/widgets/_numeric-input.scss b/src/styles/components/widgets/_numeric-input.scss index f43b6ab26..aca76a1a7 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; From e573f95e60172e9d5e99686fe4069e9a0232b063 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Wed, 15 Nov 2017 17:11:51 -0500 Subject: [PATCH 21/27] delete all the unused constants --- src/lib/constants.js | 440 ------------------------------------------- 1 file changed, 440 deletions(-) diff --git a/src/lib/constants.js b/src/lib/constants.js index 807b86b90..1f6a0629b 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -22,443 +22,3 @@ export const multiValueText = { "Common Case: An 'All' tab might display this message " + 'because the X and Y tabs contain different settings.', }; - -/* -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 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 = [ - { - mode: EDIT_MODE.GRAPH, - text: "Graph", - }, - { - mode: EDIT_MODE.STYLE, - text: "Style", - }, - { - mode: EDIT_MODE.ANALYSIS, - text: "Analysis", - }, - { - mode: EDIT_MODE.JSON, - text: "JSON", - }, - { - mode: EDIT_MODE.EXPORT, - text: "Export", - }, -]; - -export const STYLE_MODE_MENU_ITEMS = [ - { - mode: STYLE_MODE.TRACES, - text: "Traces", - }, - { - mode: STYLE_MODE.LAYOUT, - text: "Layout", - }, - { - mode: STYLE_MODE.NOTES, - text: "Notes", - }, - { - mode: STYLE_MODE.AXES, - text: "Axes", - }, - { - mode: STYLE_MODE.LEGEND, - text: "Legend", - }, - { - mode: STYLE_MODE.COLOR_BARS, - text: "Color Bars", - }, - { - mode: STYLE_MODE.SHAPES, - text: "Shapes", - }, - { - mode: STYLE_MODE.MAPBOX_LAYERS, - text: "GeoJSON", - }, - { - mode: STYLE_MODE.IMAGES, - text: "Images", - }, - { - mode: STYLE_MODE.MOBILE, - text: "Mobile", - }, -]; - -export const GRAPH_MODE_MENU_ITEMS = [ - { - mode: GRAPH_MODE.CREATE, - text: "create", - }, - { - mode: GRAPH_MODE.FILTER, - text: "filter", - }, - { - mode: GRAPH_MODE.GROUPBY, - text: "group", - }, -]; - -// 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", -}; - -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", - }, -]; - -// Layout specification for CategorizedSelectTrace -export const CHART_CATEGORY = { - BUSINESS: "BUSINESS", - SCIENCE: "SCIENCE", - CHARTS_3D: "CHARTS_3D", - FINANCIAL: "FINANCIAL", - STATISTICS: "STATISTICS", - MAPS: "MAPS", -}; - -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" }, -]; - -// ShareModalTabs -export const SHARE_MODAL_TAB_OPTIONS = { - LINK_AND_PRIVACY: "Link & Privacy", - COLLABORATORS: "Collaborate", - EMBED: "Embed", -}; - -export const SHARE_MODAL_TABS = Object.keys(SHARE_MODAL_TAB_OPTIONS).map(k => { - return SHARE_MODAL_TAB_OPTIONS[k]; -}); - -// 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"; - -export const MAPBOX_ERROR_TYPES = { - INVALID_JSON: "INVALID_JSON", - FAILED_REQUEST: "FAILED_REQUEST", - FAILED_PARSING: "FAILED_PARSING", - UNKNOWN: "UNKNOWN", -}; - -export const WORKSPACE_PLOT_ID = "js-main-plotly-workspace-plot"; - -export const WORKSPACE_CONTAINER = document.getElementById("main"); -export const WORKSPACE_PLACEHOLDER = document.getElementById( - "placeholderworkspace" -); - -const IS_TRANSFORM = true; - -// 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], -]; - -export const ANALYSES_TYPES = ANALYSES.reduce((accum, [name, type]) => { - accum[name] = type; - return accum; -}, {}); - -export const ANALYSES_TYPES_TO_LABELS = ANALYSES.reduce( - (accum, [, type, label]) => { - accum[type] = label; - return accum; - }, - {} -); - -// used by WorkspaceActions to search for linked transforms inside traces -export const TRANSFORM_TYPES = ANALYSES.filter( - ([, , , isTransform]) => isTransform -).map(([, type]) => type); - -// Constants relating to the user interface - -export const RETURN_KEY = "Enter"; -export const ESCAPE_KEY = "Escape"; -export const COMMAND_KEY = "Meta"; -export const CONTROL_KEY = "Control"; -*/ From 8afad28e8bc37867ff84280dc04026cb539db63a Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Wed, 15 Nov 2017 17:10:20 -0500 Subject: [PATCH 22/27] small fixes for props and truthy checks in components --- src/components/fields/DataSelector.js | 4 +++- src/components/fields/Numeric.js | 2 +- src/components/fields/TraceSelector.js | 6 +++++- src/components/widgets/EditableText.js | 4 ++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/fields/DataSelector.js b/src/components/fields/DataSelector.js index 1f31b244b..7189b56bb 100644 --- a/src/components/fields/DataSelector.js +++ b/src/components/fields/DataSelector.js @@ -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/Numeric.js b/src/components/fields/Numeric.js index 9632515d1..7a6a20496 100644 --- a/src/components/fields/Numeric.js +++ b/src/components/fields/Numeric.js @@ -28,7 +28,7 @@ Numeric.propTypes = { fullValue: PropTypes.func, min: PropTypes.number, max: PropTypes.number, - showArrows: PropTypes.number, + showArrows: PropTypes.bool, step: PropTypes.number, updatePlot: PropTypes.func, ...Field.propTypes, diff --git a/src/components/fields/TraceSelector.js b/src/components/fields/TraceSelector.js index 3815b8e55..a5dbb585b 100644 --- a/src/components/fields/TraceSelector.js +++ b/src/components/fields/TraceSelector.js @@ -11,7 +11,11 @@ class TraceSelector extends Component { this.fullValue = this.fullValue.bind(this); const fillMeta = props.getValObject('fill'); - this.fillTypes = fillMeta.values.filter(v => v !== 'none'); + if (fillMeta) { + this.fillTypes = fillMeta.values.filter(v => v !== 'none'); + } else { + this.fillTypes = []; + } } updatePlot(value) { 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, From d022316dbe9155cc5b78fd213aa5ce07256f1c62 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Wed, 15 Nov 2017 13:06:59 -0500 Subject: [PATCH 23/27] introduce SymbolSelector --- src/DefaultEditor.js | 19 +- src/components/fields/SymbolSelector.js | 58 +++ src/components/fields/index.js | 2 + src/components/index.js | 2 + src/components/widgets/SymbolSelector.js | 143 +++++++ src/index.js | 2 + src/lib/constants.js | 397 ++++++++++++++++++ .../components/fields/_symbolselector.scss | 38 ++ 8 files changed, 648 insertions(+), 13 deletions(-) create mode 100644 src/components/fields/SymbolSelector.js create mode 100644 src/components/widgets/SymbolSelector.js create mode 100644 src/styles/components/fields/_symbolselector.scss diff --git a/src/DefaultEditor.js b/src/DefaultEditor.js index 58ecd1c96..5139434f1 100644 --- a/src/DefaultEditor.js +++ b/src/DefaultEditor.js @@ -143,24 +143,17 @@ class DefaultEditor extends Component {
- - - - + + - - + + +
- - - + { + 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/index.js b/src/components/fields/index.js index 3a5dcfeff..631d12783 100644 --- a/src/components/fields/index.js +++ b/src/components/fields/index.js @@ -7,6 +7,7 @@ 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 { @@ -19,5 +20,6 @@ export { Radio, DataSelector, Numeric, + SymbolSelector, TraceSelector, }; diff --git a/src/components/index.js b/src/components/index.js index 8b986c9da..c594c8c0b 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -8,6 +8,7 @@ import { Radio, DataSelector, Numeric, + SymbolSelector, TraceSelector, } from './fields'; @@ -29,6 +30,7 @@ export { PanelMenuWrapper, Radio, Section, + SymbolSelector, TraceAccordion, TraceSelector, }; 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 6fd85f805..cf273d77c 100644 --- a/src/index.js +++ b/src/index.js @@ -25,6 +25,7 @@ import { Radio, Section, MenuPanel, + SymbolSelector, TraceAccordion, TraceSelector, } from './components'; @@ -47,6 +48,7 @@ export { PanelMenuWrapper, Radio, Section, + SymbolSelector, TraceAccordion, TraceSelector, connectAxesToLayout, diff --git a/src/lib/constants.js b/src/lib/constants.js index 1f6a0629b..10093cb01 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -22,3 +22,400 @@ export const multiValueText = { "Common Case: An 'All' tab might display this message " + 'because the X and Y tabs contain different settings.', }; + +export const SYMBOLS = [ + { + value: 'circle', + alias: 0, + label: 'M5,0A5,5 0 1,1 0,-5A5,5 0 0,1 5,0Z', + threeD: true, + gl: true, + }, + { + 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, + }, + { + 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}, + { + value: 'square-open', + alias: 1, + label: 'M5,5H-5V-5H5Z', + fill: 'none', + threeD: true, + gl: true, + }, + { + value: 'square-open-dot', + alias: 1, + label: 'M5,5H-5V-5H5ZM0,0.5L0.5,0L0,-0.5L-0.5,0Z', + fill: 'none', + }, + + { + value: 'diamond', + alias: 2, + label: 'M6.5,0L0,6.5L-6.5,0L0,-6.5Z', + threeD: true, + gl: true, + }, + { + value: 'diamond-open', + alias: 2, + label: 'M6.5,0L0,6.5L-6.5,0L0,-6.5Z', + fill: 'none', + threeD: true, + gl: true, + }, + { + 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', + }, + + { + value: 'cross', + alias: 3, + label: 'M6,2H2V6H-2V2H-6V-2H-2V-6H2V-2H6Z', + threeD: true, + gl: true, + }, + { + value: 'cross-open', + alias: 3, + label: 'M6,2H2V6H-2V2H-6V-2H-2V-6H2V-2H6Z', + fill: 'none', + gl: true, + }, + + { + 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, + }, + { + 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}, + { + 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}, + { + 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}, + { + value: 'triangle-left-open', + alias: 7, + label: 'M2.5,-5.77V5.77L-5,0Z', + fill: 'none', + gl: true, + }, + + {value: 'triangle-right', alias: 8, label: 'M-2.5,-5.77V5.77L5,0Z', gl: true}, + { + 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}, + { + 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}, + { + value: 'triangle-se-open', + alias: 10, + label: 'M3,-6V3H-6Z', + fill: 'none', + gl: true, + }, + + {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, + }, + + {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, + }, + + { + 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, + }, + + { + 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, + }, + + { + 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, + }, + + { + 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', + }, + + { + 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, + }, + + { + 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', + }, + + { + 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', + }, + + { + 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', + }, + + { + 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', + }, + + { + 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', + }, + + { + 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, + }, + + {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', + }, + + {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, + }, + + { + 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', + }, + + { + 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/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; + } + } From 21fb7ecc72acc30545ee9b600265ec9ca0a05285 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Wed, 15 Nov 2017 23:26:23 -0500 Subject: [PATCH 24/27] TraceMarkerSection handles naming section after trace types --- src/DefaultEditor.js | 12 +++++- src/components/containers/Section.js | 11 +++-- .../containers/TraceMarkerSection.js | 41 +++++++++++++++++++ src/components/containers/index.js | 3 +- src/components/index.js | 10 ++++- src/index.js | 2 + src/styles/components/fields/_field.scss | 2 +- .../components/widgets/_numeric-input.scss | 2 +- 8 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 src/components/containers/TraceMarkerSection.js diff --git a/src/DefaultEditor.js b/src/DefaultEditor.js index 5139434f1..ff06faa10 100644 --- a/src/DefaultEditor.js +++ b/src/DefaultEditor.js @@ -17,6 +17,7 @@ import { MenuPanel, SymbolSelector, TraceAccordion, + TraceMarkerSection, TraceSelector, } from './components'; import {DEFAULT_FONTS} from './constants'; @@ -142,14 +143,21 @@ class DefaultEditor extends Component {
-
+ + -
+
diff --git a/src/components/containers/Section.js b/src/components/containers/Section.js index b5e8aaeee..264f390ad 100644 --- a/src/components/containers/Section.js +++ b/src/components/containers/Section.js @@ -45,9 +45,14 @@ class Section extends Component { } const isAttr = Boolean(child.props.attr); - const plotProps = isAttr - ? unpackPlotProps(child.props, context, child.type) - : {isVisible: true}; + 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)); 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/index.js b/src/components/containers/index.js index 73952bf73..7284a63eb 100644 --- a/src/components/containers/index.js +++ b/src/components/containers/index.js @@ -3,5 +3,6 @@ import Fold from './Fold'; import Panel from './Panel'; import Section from './Section'; import TraceAccordion from './TraceAccordion'; +import TraceMarkerSection from './TraceMarkerSection'; -export {MenuPanel, Fold, Panel, Section, TraceAccordion}; +export {MenuPanel, Fold, Panel, Section, TraceAccordion, TraceMarkerSection}; diff --git a/src/components/index.js b/src/components/index.js index c594c8c0b..4a2cee00f 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -12,7 +12,14 @@ import { TraceSelector, } from './fields'; -import {MenuPanel, Fold, Panel, Section, TraceAccordion} from './containers'; +import { + MenuPanel, + Fold, + Panel, + Section, + TraceAccordion, + TraceMarkerSection, +} from './containers'; import PanelMenuWrapper from './PanelMenuWrapper'; export { @@ -32,5 +39,6 @@ export { Section, SymbolSelector, TraceAccordion, + TraceMarkerSection, TraceSelector, }; diff --git a/src/index.js b/src/index.js index cf273d77c..1e348d59a 100644 --- a/src/index.js +++ b/src/index.js @@ -27,6 +27,7 @@ import { MenuPanel, SymbolSelector, TraceAccordion, + TraceMarkerSection, TraceSelector, } from './components'; @@ -50,6 +51,7 @@ export { Section, SymbolSelector, TraceAccordion, + TraceMarkerSection, TraceSelector, connectAxesToLayout, connectLayoutToPlot, diff --git a/src/styles/components/fields/_field.scss b/src/styles/components/fields/_field.scss index 520945caa..ef5a68dd0 100644 --- a/src/styles/components/fields/_field.scss +++ b/src/styles/components/fields/_field.scss @@ -35,7 +35,7 @@ .field__widget { width: 64%; - padding-left: 18px; + padding-left: 12px; display: inline-block; padding-right: 12px; } diff --git a/src/styles/components/widgets/_numeric-input.scss b/src/styles/components/widgets/_numeric-input.scss index aca76a1a7..853bf29c7 100644 --- a/src/styles/components/widgets/_numeric-input.scss +++ b/src/styles/components/widgets/_numeric-input.scss @@ -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; From e0e0fd52420dede5258556a4d08eba6c96cd07db Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Wed, 15 Nov 2017 23:29:24 -0500 Subject: [PATCH 25/27] CanvasSize --- src/DefaultEditor.js | 34 ++++++++++++++++++++++++++++- src/components/fields/CanvasSize.js | 25 +++++++++++++++++++++ src/components/fields/index.js | 2 ++ src/components/index.js | 2 ++ src/index.js | 2 ++ 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/components/fields/CanvasSize.js diff --git a/src/DefaultEditor.js b/src/DefaultEditor.js index ff06faa10..4a8513011 100644 --- a/src/DefaultEditor.js +++ b/src/DefaultEditor.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { AxesSelector, AxesRange, + CanvasSize, ColorPicker, DataSelector, Dropdown, @@ -176,12 +177,43 @@ class DefaultEditor extends Component { - + + + + + + + + + + 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/index.js b/src/components/fields/index.js index 631d12783..2d3d9e199 100644 --- a/src/components/fields/index.js +++ b/src/components/fields/index.js @@ -1,5 +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'; @@ -13,6 +14,7 @@ import TraceSelector from './TraceSelector'; export { AxesRange, AxesSelector, + CanvasSize, ColorPicker, Dropdown, Flaglist, diff --git a/src/components/index.js b/src/components/index.js index 4a2cee00f..42ac7373e 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -1,6 +1,7 @@ import { AxesRange, AxesSelector, + CanvasSize, ColorPicker, Dropdown, Flaglist, @@ -26,6 +27,7 @@ export { AxesSelector, AxesRange, MenuPanel, + CanvasSize, ColorPicker, DataSelector, Dropdown, diff --git a/src/index.js b/src/index.js index 1e348d59a..15c16f022 100644 --- a/src/index.js +++ b/src/index.js @@ -12,6 +12,7 @@ import {EDITOR_ACTIONS} from './constants'; import { AxesRange, AxesSelector, + CanvasSize, ColorPicker, DataSelector, Dropdown, @@ -35,6 +36,7 @@ export { AxesRange, AxesSelector, MenuPanel, + CanvasSize, ColorPicker, DataSelector, Dropdown, From cf4078415e7031208c8e2635d090e8ab8429bef4 Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Thu, 16 Nov 2017 14:20:13 -0500 Subject: [PATCH 26/27] pull trace handling from DefaultEditor into TraceSelector fixes #56 --- src/DefaultEditor.js | 70 ++++++++------------- src/components/containers/TraceAccordion.js | 25 ++++---- src/components/fields/TraceSelector.js | 65 +++++++++++++++++-- 3 files changed, 98 insertions(+), 62 deletions(-) diff --git a/src/DefaultEditor.js b/src/DefaultEditor.js index 4a8513011..629b654e4 100644 --- a/src/DefaultEditor.js +++ b/src/DefaultEditor.js @@ -28,32 +28,6 @@ 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 as 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; @@ -65,7 +39,6 @@ class DefaultEditor extends Component { label="Plot Type" attr="type" clearable={false} - options={this.traceOptions} show /> @@ -117,6 +90,18 @@ class DefaultEditor extends Component {
+
+ +
+
- - - - - - - - - - - - - - - + {/* + + + + + + + + + + + + + + */} @@ -378,7 +363,6 @@ class DefaultEditor extends Component { DefaultEditor.contextTypes = { dataSourceNames: PropTypes.array.isRequired, - plotSchema: PropTypes.object.isRequired, }; export default localize(DefaultEditor); 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/fields/TraceSelector.js b/src/components/fields/TraceSelector.js index a5dbb585b..95dec45b7 100644 --- a/src/components/fields/TraceSelector.js +++ b/src/components/fields/TraceSelector.js @@ -4,20 +4,70 @@ 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) { - super(props); + constructor(props, context) { + super(props, context); this.updatePlot = this.updatePlot.bind(this); this.fullValue = this.fullValue.bind(this); - const fillMeta = props.getValObject('fill'); + let fillMeta; + if (props.getValObject) { + fillMeta = props.getValObject('fill'); + } if (fillMeta) { this.fillTypes = fillMeta.values.filter(v => v !== 'none'); } else { - this.fillTypes = []; + 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) { let update; if (value === 'line') { @@ -59,14 +109,19 @@ class TraceSelector extends Component { const props = Object.assign({}, this.props, { fullValue: this.fullValue, updatePlot: this.updatePlot, + options: this.traceOptions, }); return ; } } +TraceSelector.contextTypes = { + plotSchema: PropTypes.object, +}; + TraceSelector.propTypes = { - getValObject: PropTypes.func.isRequired, + getValObject: PropTypes.func, container: PropTypes.object.isRequired, fullValue: PropTypes.func.isRequired, updateContainer: PropTypes.func, From 35784a414ea870cd6a25e9349c704c5a77b78fce Mon Sep 17 00:00:00 2001 From: bpostlethwaite Date: Thu, 16 Nov 2017 14:22:02 -0500 Subject: [PATCH 27/27] remove requirement that plotly.js is passed into Editor fixes #86 --- src/PlotlyEditor.js | 10 ++++--- .../containers/__tests__/Layout-test.js | 4 +-- .../containers/__tests__/Section-test.js | 26 ++++------------- src/lib/connectAxesToLayout.js | 28 +++++++++++-------- src/lib/connectLayoutToPlot.js | 13 ++++++--- src/lib/connectTraceToPlot.js | 14 +++++++--- src/lib/unpackPlotProps.js | 17 +++++++---- 7 files changed, 60 insertions(+), 52 deletions(-) diff --git a/src/PlotlyEditor.js b/src/PlotlyEditor.js index 375cbb868..31383ac11 100644 --- a/src/PlotlyEditor.js +++ b/src/PlotlyEditor.js @@ -12,7 +12,9 @@ class PlotlyEditor extends Component { noShame({plotly: this.props.plotly}); // we only need to compute this once. - this.plotSchema = this.props.plotly.PlotSchema.get(); + if (this.props.plotly) { + this.plotSchema = this.props.plotly.PlotSchema.get(); + } } getChildContext() { @@ -51,7 +53,7 @@ class PlotlyEditor extends Component { PlotlyEditor.propTypes = { onUpdate: PropTypes.func, - plotly: PropTypes.object.isRequired, + plotly: PropTypes.object, graphDiv: PropTypes.object, locale: PropTypes.string, dataSources: PropTypes.object, @@ -72,8 +74,8 @@ PlotlyEditor.childContextTypes = { layout: PropTypes.object, locale: PropTypes.string, onUpdate: PropTypes.func, - plotSchema: PropTypes.object.isRequired, - plotly: PropTypes.object.isRequired, + plotSchema: PropTypes.object, + plotly: PropTypes.object, }; export default PlotlyEditor; 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 6ab8826e3..3bb47a7e0 100644 --- a/src/components/containers/__tests__/Section-test.js +++ b/src/components/containers/__tests__/Section-test.js @@ -2,7 +2,7 @@ import React from 'react'; import Section from '../Section'; 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( - + @@ -93,11 +81,7 @@ describe('Section', () => { it('will render first menuPanel even with no visible attrs', () => { const wrapper = mount( - +
INFO diff --git a/src/lib/connectAxesToLayout.js b/src/lib/connectAxesToLayout.js index 27e38ba9c..2af048572 100644 --- a/src/lib/connectAxesToLayout.js +++ b/src/lib/connectAxesToLayout.js @@ -34,10 +34,12 @@ function deepCopyPublic(value) { /* * Test that we can connectLayoutToPlot(connectAxesToLayout(Panel)) */ -function setMultiValuedContainer(intoObj, fromObj, key, searchArrays) { +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) === '_') || @@ -126,18 +128,22 @@ export default function connectAxesToLayout(WrappedComponent) { setLocals(nextProps, nextState, nextContext) { const {plotly, graphDiv, container, fullContainer} = nextContext; const {axesTarget} = nextState; - this.axes = plotly.Axes.list(graphDiv); + 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) - ) - ); + 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. @@ -214,8 +220,8 @@ export default function connectAxesToLayout(WrappedComponent) { container: PropTypes.object.isRequired, fullContainer: PropTypes.object.isRequired, graphDiv: PropTypes.object.isRequired, - plotly: PropTypes.object.isRequired, - updateContainer: PropTypes.func.isRequired, + plotly: PropTypes.object, + updateContainer: PropTypes.func, }; AxesConnectedComponent.childContextTypes = { 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/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/unpackPlotProps.js b/src/lib/unpackPlotProps.js index 46f6190cd..1d53e5b0f 100644 --- a/src/lib/unpackPlotProps.js +++ b/src/lib/unpackPlotProps.js @@ -34,7 +34,10 @@ export default function unpackPlotProps(props, context, ComponentClass) { } // 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}); @@ -61,11 +64,13 @@ export default function unpackPlotProps(props, context, ComponentClass) { 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; + } } // Give Component Classes the space to modify plotProps: