diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a60ccc9f..8233eaf9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1136,6 +1136,148 @@ "@emotion/weak-memoize": "0.2.2" } }, + "@emotion/core": { + "version": "10.0.22", + "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.22.tgz", + "integrity": "sha512-7eoP6KQVUyOjAkE6y4fdlxbZRA4ILs7dqkkm6oZUJmihtHv0UBq98VgPirq9T8F9K2gKu0J/au/TpKryKMinaA==", + "requires": { + "@babel/runtime": "^7.5.5", + "@emotion/cache": "^10.0.17", + "@emotion/css": "^10.0.22", + "@emotion/serialize": "^0.11.12", + "@emotion/sheet": "0.9.3", + "@emotion/utils": "0.11.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.1.tgz", + "integrity": "sha512-SQ0sS7KUJDvgCI2cpZG0nJygO6002oTbhgSuw4WcocsnbxLwL5Q8I3fqbJdyBAc3uFrWZiR2JomseuxSuci3SQ==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, + "@emotion/cache": { + "version": "10.0.19", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.19.tgz", + "integrity": "sha512-BoiLlk4vEsGBg2dAqGSJu0vJl/PgVtCYLBFJaEO8RmQzPugXewQCXZJNXTDFaRlfCs0W+quesayav4fvaif5WQ==", + "requires": { + "@emotion/sheet": "0.9.3", + "@emotion/stylis": "0.8.4", + "@emotion/utils": "0.11.2", + "@emotion/weak-memoize": "0.2.4" + } + }, + "@emotion/hash": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.3.tgz", + "integrity": "sha512-14ZVlsB9akwvydAdaEnVnvqu6J2P6ySv39hYyl/aoB6w/V+bXX0tay8cF6paqbgZsN2n5Xh15uF4pE+GvE+itw==" + }, + "@emotion/memoize": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.3.tgz", + "integrity": "sha512-2Md9mH6mvo+ygq1trTeVp2uzAKwE2P7In0cRpD/M9Q70aH8L+rxMLbb3JCN2JoSWsV2O+DdFjfbbXoMoLBczow==" + }, + "@emotion/serialize": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.14.tgz", + "integrity": "sha512-6hTsySIuQTbDbv00AnUO6O6Xafdwo5GswRlMZ5hHqiFx+4pZ7uGWXUQFW46Kc2taGhP89uXMXn/lWQkdyTosPA==", + "requires": { + "@emotion/hash": "0.7.3", + "@emotion/memoize": "0.7.3", + "@emotion/unitless": "0.7.4", + "@emotion/utils": "0.11.2", + "csstype": "^2.5.7" + } + }, + "@emotion/sheet": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.3.tgz", + "integrity": "sha512-c3Q6V7Df7jfwSq5AzQWbXHa5soeE4F5cbqi40xn0CzXxWW9/6Mxq48WJEtqfWzbZtW9odZdnRAkwCQwN12ob4A==" + }, + "@emotion/stylis": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.4.tgz", + "integrity": "sha512-TLmkCVm8f8gH0oLv+HWKiu7e8xmBIaokhxcEKPh1m8pXiV/akCiq50FvYgOwY42rjejck8nsdQxZlXZ7pmyBUQ==" + }, + "@emotion/unitless": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.4.tgz", + "integrity": "sha512-kBa+cDHOR9jpRJ+kcGMsysrls0leukrm68DmFQoMIWQcXdr2cZvyvypWuGYT7U+9kAExUE7+T7r6G3C3A6L8MQ==" + }, + "@emotion/utils": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.2.tgz", + "integrity": "sha512-UHX2XklLl3sIaP6oiMmlVzT0J+2ATTVpf0dHQVyPJHTkOITvXfaSqnRk6mdDhV9pR8T/tHc3cex78IKXssmzrA==" + }, + "@emotion/weak-memoize": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.4.tgz", + "integrity": "sha512-6PYY5DVdAY1ifaQW6XYTnOMihmBVT27elqSjEoodchsGjzYlEsTQMcEhSud99kVawatyTZRTiVkJ/c6lwbQ7nA==" + } + } + }, + "@emotion/css": { + "version": "10.0.22", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.22.tgz", + "integrity": "sha512-8phfa5mC/OadBTmGpMpwykIVH0gFCbUoO684LUkyixPq4F1Wwri7fK5Xlm8lURNBrd2TuvTbPUGxFsGxF9UacA==", + "requires": { + "@emotion/serialize": "^0.11.12", + "@emotion/utils": "0.11.2", + "babel-plugin-emotion": "^10.0.22" + }, + "dependencies": { + "@emotion/hash": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.3.tgz", + "integrity": "sha512-14ZVlsB9akwvydAdaEnVnvqu6J2P6ySv39hYyl/aoB6w/V+bXX0tay8cF6paqbgZsN2n5Xh15uF4pE+GvE+itw==" + }, + "@emotion/memoize": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.3.tgz", + "integrity": "sha512-2Md9mH6mvo+ygq1trTeVp2uzAKwE2P7In0cRpD/M9Q70aH8L+rxMLbb3JCN2JoSWsV2O+DdFjfbbXoMoLBczow==" + }, + "@emotion/serialize": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.14.tgz", + "integrity": "sha512-6hTsySIuQTbDbv00AnUO6O6Xafdwo5GswRlMZ5hHqiFx+4pZ7uGWXUQFW46Kc2taGhP89uXMXn/lWQkdyTosPA==", + "requires": { + "@emotion/hash": "0.7.3", + "@emotion/memoize": "0.7.3", + "@emotion/unitless": "0.7.4", + "@emotion/utils": "0.11.2", + "csstype": "^2.5.7" + } + }, + "@emotion/unitless": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.4.tgz", + "integrity": "sha512-kBa+cDHOR9jpRJ+kcGMsysrls0leukrm68DmFQoMIWQcXdr2cZvyvypWuGYT7U+9kAExUE7+T7r6G3C3A6L8MQ==" + }, + "@emotion/utils": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.2.tgz", + "integrity": "sha512-UHX2XklLl3sIaP6oiMmlVzT0J+2ATTVpf0dHQVyPJHTkOITvXfaSqnRk6mdDhV9pR8T/tHc3cex78IKXssmzrA==" + }, + "babel-plugin-emotion": { + "version": "10.0.23", + "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.23.tgz", + "integrity": "sha512-1JiCyXU0t5S2xCbItejCduLGGcKmF3POT0Ujbexog2MI4IlRcIn/kWjkYwCUZlxpON0O5FC635yPl/3slr7cKQ==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@emotion/hash": "0.7.3", + "@emotion/memoize": "0.7.3", + "@emotion/serialize": "^0.11.14", + "babel-plugin-macros": "^2.0.0", + "babel-plugin-syntax-jsx": "^6.18.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^1.0.5", + "find-root": "^1.1.0", + "source-map": "^0.5.7" + } + } + } + }, "@emotion/hash": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.1.tgz", @@ -4754,7 +4896,8 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true }, "aproba": { "version": "1.2.0", @@ -4775,12 +4918,14 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "optional": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4795,17 +4940,20 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "optional": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -4922,7 +5070,8 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "optional": true }, "ini": { "version": "1.3.5", @@ -4934,6 +5083,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -4948,6 +5098,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -4955,12 +5106,14 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "optional": true }, "minipass": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -4979,6 +5132,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5059,7 +5213,8 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5071,6 +5226,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "optional": true, "requires": { "wrappy": "1" } @@ -5156,7 +5312,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5192,6 +5349,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5211,6 +5369,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5254,12 +5413,14 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "optional": true }, "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "optional": true } } }, @@ -9665,7 +9826,8 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "optional": true }, "aproba": { "version": "1.2.0", @@ -9686,12 +9848,14 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "optional": true }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -9706,17 +9870,20 @@ "code-point-at": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "optional": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "optional": true }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -9833,7 +10000,8 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "optional": true }, "ini": { "version": "1.3.5", @@ -9845,6 +10013,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -9859,6 +10028,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -9866,12 +10036,14 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "optional": true }, "minipass": { "version": "2.3.5", "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -9890,6 +10062,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "optional": true, "requires": { "minimist": "0.0.8" } @@ -9970,7 +10143,8 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "optional": true }, "object-assign": { "version": "4.1.1", @@ -9982,6 +10156,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "optional": true, "requires": { "wrappy": "1" } @@ -10067,7 +10242,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -10103,6 +10279,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -10122,6 +10299,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -10165,12 +10343,14 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "optional": true }, "yallist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "optional": true } } } @@ -11565,6 +11745,11 @@ } } }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" + }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -13942,6 +14127,14 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.1.tgz", "integrity": "sha512-V9yoTr6MeZXPPd4nV/05eCBvGH9cGzc52FN8fs0O0TVQ3HYYf1n7EgZVtHbldRq5xU9zEzoXIITjYNIfxDDdUw==" }, + "react-input-autosize": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.2.tgz", + "integrity": "sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw==", + "requires": { + "prop-types": "^15.5.8" + } + }, "react-is": { "version": "16.8.6", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz", @@ -13973,6 +14166,11 @@ "resolved": "https://registry.npmjs.org/react-leaflet-control/-/react-leaflet-control-2.1.1.tgz", "integrity": "sha512-3fx4uSRFtDaFNa1yq62DCxdc9kR3Lk4V7R0lHqbKfPTEIOvPR7VFvk3xzcSnSB/0LAYO8R96Pwcs1c6knyL/Hw==" }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "react-redux": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-6.0.1.tgz", @@ -14199,6 +14397,32 @@ } } }, + "react-select": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-3.0.8.tgz", + "integrity": "sha512-v9LpOhckLlRmXN5A6/mGGEft4FMrfaBFTGAnuPHcUgVId7Je42kTq9y0Z+Ye5z8/j0XDT3zUqza8gaRaI1PZIg==", + "requires": { + "@babel/runtime": "^7.4.4", + "@emotion/cache": "^10.0.9", + "@emotion/core": "^10.0.9", + "@emotion/css": "^10.0.9", + "memoize-one": "^5.0.0", + "prop-types": "^15.6.0", + "react-input-autosize": "^2.2.2", + "react-transition-group": "^2.2.1" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + }, "react-vis": { "version": "1.11.7", "resolved": "https://registry.npmjs.org/react-vis/-/react-vis-1.11.7.tgz", diff --git a/frontend/package.json b/frontend/package.json index 56f7f351..91c3ac17 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,6 +24,7 @@ "react-leaflet-control": "^2.1.1", "react-redux": "^6.0.1", "react-scripts": "3.1.1", + "react-select": "^3.0.8", "react-vis": "^1.11.7", "redux": "^4.0.1", "redux-first-router": "^2.1.1", diff --git a/frontend/src/components/ControlPanel.jsx b/frontend/src/components/ControlPanel.jsx index be7ed323..5a98da38 100644 --- a/frontend/src/components/ControlPanel.jsx +++ b/frontend/src/components/ControlPanel.jsx @@ -14,6 +14,7 @@ import StartStopIcon from '@material-ui/icons/DirectionsTransit'; import EndStopIcon from '@material-ui/icons/Flag'; import { getDownstreamStopIds } from '../helpers/mapGeometry'; import { Colors } from '../UIConstants'; +import ReactSelect from './ReactSelect'; const useStyles = makeStyles(theme => ({ root: { @@ -23,6 +24,7 @@ const useStyles = makeStyles(theme => ({ formControl: { margin: theme.spacing(1), minWidth: 120, + maxWidth: '100%', }, })); @@ -53,8 +55,8 @@ function ControlPanel(props) { const selectedRoute = getSelectedRouteInfo(); - function onSelectFirstStop(event) { - const startStopId = event.target.value; + function onSelectFirstStop(option) { + const startStopId = option.value.stopId; props.dispatch({ type: 'ROUTESCREEN', @@ -65,8 +67,8 @@ function ControlPanel(props) { }); } - function onSelectSecondStop(event) { - const endStopId = event.target.value; + function onSelectSecondStop(option) { + const endStopId = option.value.stopId; props.dispatch({ type: 'ROUTESCREEN', @@ -189,35 +191,35 @@ function ControlPanel(props) { - From Stop - + handleItemMouseOver={handleItemMouseOver} + handleItemMouseOut={handleItemMouseOut} + /> @@ -225,35 +227,33 @@ function ControlPanel(props) { - To Stop - + ).title, + }))} + stopId={graphParams.endStopId} + onOpen={() => setAllowHover(true)} + onClose={handleSelectClose} + handleItemMouseOver={handleItemMouseOver} + handleItemMouseOut={handleItemMouseOut} + /> diff --git a/frontend/src/components/ReactSelect.jsx b/frontend/src/components/ReactSelect.jsx new file mode 100644 index 00000000..4cc30ec2 --- /dev/null +++ b/frontend/src/components/ReactSelect.jsx @@ -0,0 +1,546 @@ +import React, { useState, useEffect, useRef } from 'react'; +import Select, { components } from 'react-select'; +import { makeStyles, createMuiTheme } from '@material-ui/core/styles'; +import Paper from '@material-ui/core/Paper'; +import Fade from '@material-ui/core/Fade'; +import Grow from '@material-ui/core/Grow'; +import TextField from '@material-ui/core/TextField'; +import MenuItem from '@material-ui/core/MenuItem'; +import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; + +const transitionDuration = 350; +const scrollHandlerDelay = 30; +const theme = createMuiTheme({ + palette: { + background: { + focus: 'rgba(0, 0, 0, 0.05)', + }, + }, +}); +const useStyles = makeStyles({ + input: { + display: 'flex', + '&:focus': { + backgroundColor: theme.palette.background.focus, + }, + }, + selectInput: { + minWidth: '100%', + }, + valueContainer: { + display: 'flex', + flex: 1, + alignItems: 'center', + minWidth: 0, + maxWidth: '100%', + }, + textContent: { + display: 'flex', + flex: 1, + whiteSpace: 'nowrap', + }, + placeholder: { + color: 'hsl(0, 0%, 75%)', + }, + menu: { + position: 'absolute', + zIndex: 1200, + }, +}); + +const selectStyles = { + dropdownIndicator: provided => ({ + ...provided, + paddingLeft: 0, + paddingRight: 0, + }), + input: (provided, state) => ({ + ...provided, + marginLeft: 0, + marginRight: 0, + maxWidth: '100%', + overflow: 'hidden', + minWidth: state.minWidth, + }), +}; + +/** + * handles keyup when textfield is focused via tab key + * input element is focused on Enter/ArrowDown + */ +function handleTextKeyUp(controlProps) { + return e => { + if (e.key === 'Enter' || e.key === 'ArrowDown') { + controlProps.selectProps.selectRef.current.focus(); + controlProps.selectProps.setMenuIsOpen(true); + } + }; +} + +function Control(props) { + const { + children, + innerProps, + selectProps: { labelRef, textRef, classes, textFieldProps }, + } = props; + const inputLabelProps = textFieldProps.InputLabelProps; + inputLabelProps.ref = labelRef; + + return ( + + ); +} + +function ValueContainer(props) { + const { + children, + selectProps: { classes }, + } = props; + const input = children[1]; + const singleValue = children[0]; + + return
{[input, singleValue]}
; +} + +function handleInputKeyUp(inputProps) { + const { + selectProps: { setTextFieldDOMRect, textRef }, + } = inputProps; + return () => setTextFieldDOMRect(textRef.current.getBoundingClientRect()); +} + +/** + * sets min-width of select input wrapper to 100% when input value exists + * allows input cursor to be moved by clicking on blank space within textfield + */ +function handleInputChange(inputProps) { + const { + onChange, + selectProps: { setInputMinWidth }, + } = inputProps; + + return e => { + onChange(e); + if (e.target.value) { + setInputMinWidth('100%'); + e.target.style.minWidth = '100%'; + } else { + setInputMinWidth(0); + } + }; +} + +function handleInputBlur(inputProps) { + return e => { + inputProps.onBlur(e); + inputProps.selectProps.setInputMinWidth(0); + }; +} + +function Input(props) { + return ( + + ); +} + +function Placeholder(props) { + const { + children, + selectProps: { classes }, + } = props; + + return ( +
+ {children} +
+ ); +} + +function SingleValue(props) { + return ( +
+ {props.children} +
+ ); +} + +function DropdownIndicator(props) { + return ( + + + + ); +} + +function Menu(props) { + const { + children, + innerProps, + selectProps: { + classes, + focusedOptionRef, + labelRef, + menuRef, + menuIsOpenTransition, + menuPlacementTop, + menuTransition, + textFieldDOMRect, + }, + } = props; + const menuStyle = {}; + const timeout = menuTransition.current ? transitionDuration : 0; + const [menuStyleRight, setMenuStyleRight] = useState(0); + const [menuStyleBottom, setMenuStyleBottom] = useState(0); + + // evaluates true if there is more space for the menu above the textfield rather than below + menuPlacementTop.current = + textFieldDOMRect.top > + document.documentElement.clientHeight - textFieldDOMRect.bottom; + + if (menuStyleRight) { + menuStyle.right = menuStyleRight; + } + + if (menuStyleBottom) { + menuStyle.bottom = menuStyleBottom; + } + + useEffect(() => { + const labelHeight = labelRef.current.clientHeight; + const inputHeight = textFieldDOMRect.height + labelHeight; + const rightWillSlice = + textFieldDOMRect.left + menuRef.current.clientWidth > window.innerWidth; + const leftWillSlice = + textFieldDOMRect.right - menuRef.current.clientWidth < 0; + const idealRightPosition = + textFieldDOMRect.right - document.documentElement.clientWidth; + + /** + * check if the right side of the menu will be outside the view + * if so change the 'right' property of the style object + * but only if the left side is not cut off in the process, otherwise default positon + */ + if (rightWillSlice && !leftWillSlice) { + if (menuStyleRight !== idealRightPosition) { + setMenuStyleRight(idealRightPosition); + } + } else if (menuStyleRight) { + setMenuStyleRight(0); + } + + if (menuPlacementTop.current) { + if (menuStyleBottom !== inputHeight) { + setMenuStyleBottom(inputHeight); + } + } else if (menuStyleBottom) { + setMenuStyleBottom(0); + } + + // temporary fix to react-select issue not setting focus to selected value by default + if (focusedOptionRef.current) { + focusedOptionRef.current.parentNode.scrollTop = + focusedOptionRef.current.offsetTop - + menuRef.current.clientHeight / 2 + + focusedOptionRef.current.clientHeight / 2; + } + }, [ + focusedOptionRef, + labelRef, + menuRef, + menuPlacementTop, + menuStyleBottom, + menuStyleRight, + textFieldDOMRect, + ]); + + return ( + + + + {children} + + + + ); +} + +function MenuList(props) { + const { + children, + selectProps: { labelRef, menuPlacementTop, textFieldDOMRect }, + } = props; + let maxHeight; + const maxHeightLimit = + document.documentElement.clientHeight - theme.spacing(2); + + // calculates appropriate max height depending on top or bottom menu placement + if (menuPlacementTop.current) { + maxHeight = + textFieldDOMRect.top - labelRef.current.clientHeight - theme.spacing(2); + } else { + maxHeight = + document.documentElement.clientHeight - + textFieldDOMRect.bottom - + theme.spacing(2); + } + if (maxHeight > maxHeightLimit) maxHeight = maxHeightLimit; + + return ( + + {children} + + ); +} + +function optionRef(optionProps) { + const { + innerRef, + isSelected, + selectProps: { focusedOptionRef }, + } = optionProps; + + return element => { + if (isSelected) focusedOptionRef.current = element; + if (innerRef) innerRef(element); + }; +} + +function Option(props) { + const { + children, + innerProps, + isFocused, + isSelected, + data: { + label, + value: { icon }, + }, + selectProps: { handleItemMouseOver, handleItemMouseOut }, + } = props; + const focusedStyle = {}; + + if (isFocused) { + handleItemMouseOver(icon, label); + if (!isSelected) focusedStyle.backgroundColor = theme.palette.action.hover; + } else { + handleItemMouseOut(icon); + } + + return ( + + {children} + + ); +} + +function handleMenuOpen( + menuTransition, + setMenuIsOpenTransition, + setMenuIsOpen, + onOpen +) { + const allowTransition = menuTransition; + + return () => { + allowTransition.current = true; + setMenuIsOpenTransition(true); + setMenuIsOpen(true); + onOpen(); + }; +} + +function handleMenuClose( + menuTransition, + setMenuIsOpenTransition, + setMenuIsOpen, + onClose +) { + const allowTransition = menuTransition; + + return () => { + allowTransition.current = true; + document.activeElement.blur(); + setMenuIsOpenTransition(false); + setTimeout(() => setMenuIsOpen(false), transitionDuration); + onClose(); + }; +} + +function filterValue(stopId) { + return option => option.value.stopId === stopId; +} + +/** + * updates textfield location on scroll/resize + * re-renders menu if open which updates menu placement and max height + */ +const reposition = {}; +function handleReposition( + eventType, + inputId, + menuTransition, + setTextFieldDOMRect, + textRef, +) { + const allowTransition = menuTransition; + reposition[eventType] = () => { + clearTimeout(window[`${inputId}Timeout`]); + window[`${inputId}Timeout`] = setTimeout( + () => { + allowTransition.current = false; + setTextFieldDOMRect(textRef.current.getBoundingClientRect()); + }, + eventType === 'scroll' ? scrollHandlerDelay : 0 + ); + }; + + return reposition[eventType]; +} + +export default function ReactSelect(props) { + const classes = useStyles(); + const labelRef = useRef(); + const menuRef = useRef(); + const selectRef = useRef(); + const textRef = useRef(); + const focusedOptionRef = useRef(); + const [menuIsOpen, setMenuIsOpen] = useState(false); + // similar to menuIsOpen, but triggers transition immediately while actual menu close is on timeout + const [menuIsOpenTransition, setMenuIsOpenTransition] = useState(false); + const menuPlacementTop = useRef(false); + // determines whether transitionDuration is used. Set to false on resize/scroll + const menuTransition = useRef(true); + // contains position and dimensions of textfield + const [textFieldDOMRect, setTextFieldDOMRect] = useState({}); + const [inputMinWidth, setInputMinWidth] = useState(0); + const replacedComponents = { + Control, + ValueContainer, + Input, + Placeholder, + SingleValue, + IndicatorSeparator: null, + DropdownIndicator, + Menu, + MenuList, + Option, + }; + + useEffect(() => { + const inputEl = document.getElementById(props.inputId); + window.addEventListener( + 'scroll', + handleReposition( + 'scroll', + props.inputId, + menuTransition, + setTextFieldDOMRect, + textRef, + ) + ); + window.addEventListener( + 'resize', + handleReposition( + 'resize', + props.inputId, + menuTransition, + setTextFieldDOMRect, + textRef, + ) + ); + inputEl.addEventListener( + 'focus', + handleReposition( + 'focus', + props.inputId, + menuTransition, + setTextFieldDOMRect, + textRef, + ) + ); + + return () => { + window.removeEventListener('scroll', reposition.scroll); + window.removeEventListener('resize', reposition.resize); + inputEl.removeEventListener('focus', reposition.focus); + }; + }, [props.inputId, setTextFieldDOMRect]); + + return ( +