diff --git a/.circleci/config.yml b/.circleci/config.yml index d9be24a1..6b70ffb3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -446,7 +446,7 @@ workflows: branches: only: - /^release-\d+(\.\d+){1,2}(-rc.*)?/ - - react_bootstrap_upgrade + - pull/1647 - store_artifacts: # for master/PR builds diff --git a/package-lock.json b/package-lock.json index 53f10e8a..8f794317 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "react-bootstrap": "^1.6.1", "react-dom": "^16.14.0", "react-transition-group": "^2.5.0", + "sass": "^1.35.1", "semver": "^5.5.0", "underscore": "^1.9.1", "valid-url": "^1.0.9", @@ -84,6 +85,7 @@ "mocha": "^5.2.0", "mocha-circleci-reporter": "0.0.3", "npm-run-all": "^4.1.5", + "sass-loader": "^10.2.0", "shebang-loader": "^0.0.1", "spectron": "^14.0.0", "style-loader": "^0.23.1", @@ -6838,7 +6840,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -8417,7 +8418,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true, "engines": { "node": ">=8" } @@ -9517,7 +9517,6 @@ "version": "3.4.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", - "dev": true, "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", @@ -9536,7 +9535,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -9548,7 +9546,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -9560,7 +9557,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -9572,7 +9568,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -9581,7 +9576,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -15548,7 +15542,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, "os": [ "darwin" ], @@ -17386,7 +17379,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -17575,7 +17567,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -17621,7 +17612,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -19974,7 +19964,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -20984,7 +20973,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -23092,7 +23080,6 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -23631,6 +23618,122 @@ "truncate-utf8-bytes": "^1.0.0" } }, + "node_modules/sass": { + "version": "1.35.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz", + "integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/sass-loader": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.2.0.tgz", + "integrity": "sha512-kUceLzC1gIHz0zNJPpqRsJyisWatGYNFRmv2CKZK2/ngMJgLqxTbXwe/hJ85luyvZkgqU3VlJ33UVF2T/0g6mw==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0", + "sass": "^1.3.0", + "webpack": "^4.36.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/sass-loader/node_modules/loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/sass-loader/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sass-loader/node_modules/schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/sass-loader/node_modules/semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sass-loader/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -33278,7 +33381,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -34592,8 +34694,7 @@ "binary-extensions": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" }, "binaryextensions": { "version": "4.15.0", @@ -35514,7 +35615,6 @@ "version": "3.4.2", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz", "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==", - "dev": true, "requires": { "anymatch": "~3.1.1", "braces": "~3.0.2", @@ -35530,7 +35630,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -35539,7 +35638,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -35548,7 +35646,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, "requires": { "is-glob": "^4.0.1" } @@ -35556,14 +35653,12 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" } @@ -40455,8 +40550,7 @@ "fsevents": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==" }, "function-bind": { "version": "1.1.1", @@ -41939,7 +42033,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "requires": { "binary-extensions": "^2.0.0" } @@ -42084,8 +42177,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-finite": { "version": "1.1.0", @@ -42119,7 +42211,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -44052,8 +44143,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "normalize-range": { "version": "0.1.2", @@ -44851,8 +44941,7 @@ "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" }, "pidtree": { "version": "0.3.1", @@ -46519,7 +46608,6 @@ "version": "3.4.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", - "dev": true, "requires": { "picomatch": "^2.2.1" } @@ -46969,6 +47057,75 @@ "truncate-utf8-bytes": "^1.0.0" } }, + "sass": { + "version": "1.35.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.35.1.tgz", + "integrity": "sha512-oCisuQJstxMcacOPmxLNiLlj4cUyN2+8xJnG7VanRoh2GOLr9RqkvI4AxA4a6LHVg/rsu+PmxXeGhrdSF9jCiQ==", + "requires": { + "chokidar": ">=3.0.0 <4.0.0" + } + }, + "sass-loader": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.2.0.tgz", + "integrity": "sha512-kUceLzC1gIHz0zNJPpqRsJyisWatGYNFRmv2CKZK2/ngMJgLqxTbXwe/hJ85luyvZkgqU3VlJ33UVF2T/0g6mw==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "schema-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz", + "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.6", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", diff --git a/package.json b/package.json index 923363ac..b29fa176 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "mocha": "^5.2.0", "mocha-circleci-reporter": "0.0.3", "npm-run-all": "^4.1.5", + "sass-loader": "^10.2.0", "shebang-loader": "^0.0.1", "spectron": "^14.0.0", "style-loader": "^0.23.1", @@ -136,6 +137,7 @@ "react-bootstrap": "^1.6.1", "react-dom": "^16.14.0", "react-transition-group": "^2.5.0", + "sass": "^1.35.1", "semver": "^5.5.0", "underscore": "^1.9.1", "valid-url": "^1.0.9", diff --git a/src/common/communication.ts b/src/common/communication.ts index eccf333c..1beabb3f 100644 --- a/src/common/communication.ts +++ b/src/common/communication.ts @@ -16,6 +16,7 @@ export const GET_CONFIGURATION = 'get-configuration'; export const UPDATE_CONFIGURATION = 'update-configuration'; export const GET_LOCAL_CONFIGURATION = 'get-local-configuration'; export const RELOAD_CONFIGURATION = 'reload-config'; +export const EMIT_CONFIGURATION = 'emit-configuration'; export const UPDATE_TEAMS = 'update-teams'; export const DARK_MODE_CHANGE = 'dark_mode_change'; @@ -78,3 +79,11 @@ export const ADD_SERVER = 'add-server'; export const FOCUS_THREE_DOT_MENU = 'focus-three-dot-menu'; export const LOADSCREEN_END = 'loadscreen-end'; + +export const OPEN_TEAMS_DROPDOWN = 'open-teams-dropdown'; +export const CLOSE_TEAMS_DROPDOWN = 'close-teams-dropdown'; +export const UPDATE_TEAMS_DROPDOWN = 'update-teams-dropdown'; +export const UPDATE_DROPDOWN_MENTIONS = 'update-dropdown-mentions'; +export const REQUEST_TEAMS_DROPDOWN_INFO = 'request-teams-dropdown-info'; +export const RECEIVE_DROPDOWN_MENU_SIZE = 'receive-dropdown-menu-size'; +export const SEND_DROPDOWN_MENU_SIZE = 'send-dropdown-menu-size'; diff --git a/src/common/utils/constants.ts b/src/common/utils/constants.ts index 145e29d6..0f7ff113 100644 --- a/src/common/utils/constants.ts +++ b/src/common/utils/constants.ts @@ -10,3 +10,9 @@ export const RELOAD_INTERVAL = 5 * SECOND; export const MAX_SERVER_RETRIES = 3; export const MAX_LOADING_SCREEN_SECONDS = 4 * SECOND; + +export const TAB_BAR_HEIGHT = 40; +export const BACK_BAR_HEIGHT = 36; +export const THREE_DOT_MENU_WIDTH = 40; +export const THREE_DOT_MENU_WIDTH_MAC = 80; +export const MENU_SHADOW_WIDTH = 24; diff --git a/src/main/appState.ts b/src/main/appState.ts index 84c2dbb1..6f2712f2 100644 --- a/src/main/appState.ts +++ b/src/main/appState.ts @@ -4,7 +4,7 @@ import events from 'events'; import {ipcMain} from 'electron'; -import {UPDATE_MENTIONS, UPDATE_TRAY, UPDATE_BADGE, SESSION_EXPIRED} from 'common/communication'; +import {UPDATE_MENTIONS, UPDATE_TRAY, UPDATE_BADGE, SESSION_EXPIRED, UPDATE_DROPDOWN_MENTIONS} from 'common/communication'; import * as WindowManager from './windows/windowManager'; @@ -32,12 +32,17 @@ const emitBadge = (expired?: boolean, mentions?: number, unreads?: boolean) => { status.emitter.emit(UPDATE_BADGE, expired, mentions, unreads); }; +const emitDropdown = (expired?: Map, mentions?: Map, unreads?: Map) => { + status.emitter.emit(UPDATE_DROPDOWN_MENTIONS, expired, mentions, unreads); +}; + export const emitStatus = () => { const expired = anyExpired(); const mentions = totalMentions(); const unreads = anyUnreads(); emitTray(expired, mentions, unreads); emitBadge(expired, mentions, unreads); + emitDropdown(status.expired, status.mentions, status.unreads); }; export const updateMentions = (serverName: string, mentions: number, unreads?: boolean) => { diff --git a/src/main/main.ts b/src/main/main.ts index e8c8cde4..9657a480 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -35,6 +35,7 @@ import { SHOW_SETTINGS_WINDOW, RELOAD_CONFIGURATION, USER_ACTIVITY_UPDATE, + EMIT_CONFIGURATION, } from 'common/communication'; import Config from 'common/config'; @@ -268,6 +269,7 @@ function handleConfigUpdate(newConfig: Config) { } ipcMain.emit('update-menu', true, config); + ipcMain.emit(EMIT_CONFIGURATION, true, newConfig.data); } function handleConfigSynchronize() { @@ -284,6 +286,8 @@ function handleConfigSynchronize() { if (app.isReady()) { WindowManager.sendToRenderer(RELOAD_CONFIGURATION); } + + ipcMain.emit(EMIT_CONFIGURATION, true, config.data); } function handleReloadConfig() { @@ -302,6 +306,8 @@ function handleDarkModeChange(darkMode: boolean) { refreshTrayImages(config.trayIconTheme); WindowManager.sendToRenderer(DARK_MODE_CHANGE, darkMode); WindowManager.updateLoadingScreenDarkMode(darkMode); + + ipcMain.emit(EMIT_CONFIGURATION, true, config.data); } // diff --git a/src/main/preload/dropdown.js b/src/main/preload/dropdown.js new file mode 100644 index 00000000..c744321d --- /dev/null +++ b/src/main/preload/dropdown.js @@ -0,0 +1,45 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +'use strict'; + +import {ipcRenderer} from 'electron'; + +import { + UPDATE_TEAMS_DROPDOWN, + REQUEST_TEAMS_DROPDOWN_INFO, + RECEIVE_DROPDOWN_MENU_SIZE, + SEND_DROPDOWN_MENU_SIZE, + SWITCH_SERVER, + CLOSE_TEAMS_DROPDOWN, + SHOW_NEW_SERVER_MODAL, +} from 'common/communication'; + +console.log('preloaded for the dropdown!'); + +window.addEventListener('message', async (event) => { + switch (event.data.type) { + case REQUEST_TEAMS_DROPDOWN_INFO: + ipcRenderer.send(REQUEST_TEAMS_DROPDOWN_INFO); + break; + case SEND_DROPDOWN_MENU_SIZE: + ipcRenderer.send(RECEIVE_DROPDOWN_MENU_SIZE, event.data.data.width, event.data.data.height); + break; + case SWITCH_SERVER: + ipcRenderer.send(SWITCH_SERVER, event.data.data); + break; + case SHOW_NEW_SERVER_MODAL: + ipcRenderer.send(SHOW_NEW_SERVER_MODAL); + break; + case CLOSE_TEAMS_DROPDOWN: + ipcRenderer.send(CLOSE_TEAMS_DROPDOWN); + break; + default: + console.log(`got a message: ${event}`); + console.log(event); + } +}); + +ipcRenderer.on(UPDATE_TEAMS_DROPDOWN, (event, teams, activeTeam, darkMode, expired, mentions, unreads) => { + window.postMessage({type: UPDATE_TEAMS_DROPDOWN, data: {teams, activeTeam, darkMode, expired, mentions, unreads}}, window.location.href); +}); diff --git a/src/main/preload/loadingScreenPreload.js b/src/main/preload/loadingScreenPreload.js index b28389bf..6b808b05 100644 --- a/src/main/preload/loadingScreenPreload.js +++ b/src/main/preload/loadingScreenPreload.js @@ -6,7 +6,13 @@ import {ipcRenderer} from 'electron'; -import {RECEIVED_LOADING_SCREEN_DATA, GET_LOADING_SCREEN_DATA, LOADING_SCREEN_ANIMATION_FINISHED, TOGGLE_LOADING_SCREEN_VISIBILITY} from 'common/communication'; +import { + RECEIVED_LOADING_SCREEN_DATA, + GET_LOADING_SCREEN_DATA, + LOADING_SCREEN_ANIMATION_FINISHED, + TOGGLE_LOADING_SCREEN_VISIBILITY, + CLOSE_TEAMS_DROPDOWN, +} from 'common/communication'; console.log('preloaded for the loading screen!'); @@ -31,3 +37,7 @@ ipcRenderer.on(GET_LOADING_SCREEN_DATA, (_, result) => { ipcRenderer.on(TOGGLE_LOADING_SCREEN_VISIBILITY, (_, toggle) => { window.postMessage({type: TOGGLE_LOADING_SCREEN_VISIBILITY, data: toggle}, window.location.href); }); + +window.addEventListener('click', () => { + ipcRenderer.send(CLOSE_TEAMS_DROPDOWN); +}); diff --git a/src/main/preload/mattermost.js b/src/main/preload/mattermost.js index 092bdc5f..07013604 100644 --- a/src/main/preload/mattermost.js +++ b/src/main/preload/mattermost.js @@ -9,7 +9,7 @@ import {ipcRenderer, webFrame} from 'electron'; import log from 'electron-log'; -import {NOTIFY_MENTION, IS_UNREAD, UNREAD_RESULT, SESSION_EXPIRED, SET_SERVER_NAME, REACT_APP_INITIALIZED, USER_ACTIVITY_UPDATE} from 'common/communication'; +import {NOTIFY_MENTION, IS_UNREAD, UNREAD_RESULT, SESSION_EXPIRED, SET_SERVER_NAME, REACT_APP_INITIALIZED, USER_ACTIVITY_UPDATE, CLOSE_TEAMS_DROPDOWN} from 'common/communication'; const UNREAD_COUNT_INTERVAL = 1000; const CLEAR_CACHE_INTERVAL = 6 * 60 * 60 * 1000; // 6 hours @@ -205,4 +205,8 @@ setInterval(() => { webFrame.clearCache(); }, CLEAR_CACHE_INTERVAL); +window.addEventListener('click', () => { + ipcRenderer.send(CLOSE_TEAMS_DROPDOWN); +}); + /* eslint-enable no-magic-numbers */ diff --git a/src/main/utils.ts b/src/main/utils.ts index 6fa8fda7..6b7cdf83 100644 --- a/src/main/utils.ts +++ b/src/main/utils.ts @@ -7,12 +7,9 @@ import path from 'path'; import {Args} from 'types/args'; -import {PRODUCTION} from 'common/utils/constants'; +import {BACK_BAR_HEIGHT, PRODUCTION, TAB_BAR_HEIGHT} from 'common/utils/constants'; import Utils from 'common/utils/util'; -const TAB_BAR_HEIGHT = 40; -const BACK_BAR_HEIGHT = 36; - export function shouldBeHiddenOnStartup(parsedArgv: Args) { if (parsedArgv.hidden) { return true; diff --git a/src/main/views/MattermostView.ts b/src/main/views/MattermostView.ts index fd7830a2..be1e9a81 100644 --- a/src/main/views/MattermostView.ts +++ b/src/main/views/MattermostView.ts @@ -73,19 +73,17 @@ export class MattermostView extends EventEmitter { this.window = win; const preload = getLocalPreload('preload.js'); - this.options = { - webPreferences: { - contextIsolation: process.env.NODE_ENV !== 'test', - preload, - additionalArguments: [ - `version=${app.getVersion()}`, - `appName=${app.name}`, - ], - enableRemoteModule: process.env.NODE_ENV === 'test', - nodeIntegration: process.env.NODE_ENV === 'test', - ...options.webPreferences, - }, - ...options, + this.options = Object.assign({}, options); + this.options.webPreferences = { + contextIsolation: process.env.NODE_ENV !== 'test', + preload, + additionalArguments: [ + `version=${app.getVersion()}`, + `appName=${app.name}`, + ], + enableRemoteModule: process.env.NODE_ENV === 'test', + nodeIntegration: process.env.NODE_ENV === 'test', + ...options.webPreferences, }; this.isVisible = false; this.view = new BrowserView(this.options); diff --git a/src/main/views/teamDropdownView.ts b/src/main/views/teamDropdownView.ts new file mode 100644 index 00000000..39a0f836 --- /dev/null +++ b/src/main/views/teamDropdownView.ts @@ -0,0 +1,114 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {BrowserView, BrowserWindow, ipcMain, IpcMainEvent} from 'electron'; +import {CombinedConfig, Team} from 'types/config'; + +import { + CLOSE_TEAMS_DROPDOWN, + EMIT_CONFIGURATION, + OPEN_TEAMS_DROPDOWN, + UPDATE_TEAMS_DROPDOWN, + UPDATE_DROPDOWN_MENTIONS, + REQUEST_TEAMS_DROPDOWN_INFO, + RECEIVE_DROPDOWN_MENU_SIZE, + SET_SERVER_KEY, +} from 'common/communication'; +import * as AppState from '../appState'; +import {TAB_BAR_HEIGHT, THREE_DOT_MENU_WIDTH, THREE_DOT_MENU_WIDTH_MAC, MENU_SHADOW_WIDTH} from 'common/utils/constants'; +import {getLocalPreload, getLocalURLString} from 'main/utils'; +import * as WindowManager from '../windows/windowManager'; + +export default class TeamDropdownView { + view: BrowserView; + bounds?: Electron.Rectangle; + teams: Team[]; + activeTeam?: string; + darkMode: boolean; + unreads?: Map; + mentions?: Map; + expired?: Map; + window: BrowserWindow; + + constructor(window: BrowserWindow, teams: Team[], darkMode: boolean) { + this.teams = teams; + this.window = window; + this.darkMode = darkMode; + + const preload = getLocalPreload('dropdown.js'); + this.view = new BrowserView({webPreferences: { + contextIsolation: process.env.NODE_ENV !== 'test', + preload, + nodeIntegration: process.env.NODE_ENV === 'test', + enableRemoteModule: process.env.NODE_ENV === 'test', + }}); + + this.view.webContents.loadURL(getLocalURLString('dropdown.html')); + + ipcMain.on(OPEN_TEAMS_DROPDOWN, this.handleOpen); + ipcMain.on(CLOSE_TEAMS_DROPDOWN, this.handleClose); + ipcMain.on(EMIT_CONFIGURATION, this.updateConfig); + ipcMain.on(REQUEST_TEAMS_DROPDOWN_INFO, this.updateDropdown); + ipcMain.on(RECEIVE_DROPDOWN_MENU_SIZE, this.handleReceivedMenuSize); + ipcMain.on(SET_SERVER_KEY, this.updateActiveTeam); + AppState.on(UPDATE_DROPDOWN_MENTIONS, this.updateMentions); + } + + updateConfig = (event: IpcMainEvent, config: CombinedConfig) => { + this.teams = config.teams; + this.darkMode = config.darkMode; + this.updateDropdown(); + } + + updateActiveTeam = (event: IpcMainEvent, name: string) => { + this.activeTeam = name; + this.updateDropdown(); + } + + updateMentions = (expired: Map, mentions: Map, unreads: Map) => { + this.unreads = unreads; + this.mentions = mentions; + this.expired = expired; + this.updateDropdown(); + } + + updateDropdown = () => { + this.view.webContents.send(UPDATE_TEAMS_DROPDOWN, this.teams, this.activeTeam, this.darkMode, this.expired, this.mentions, this.unreads); + } + + handleOpen = () => { + this.window.addBrowserView(this.view); + const bounds = this.view.getBounds(); + this.view.setBounds(this.getBounds(bounds.width, bounds.height)); + this.window.setTopBrowserView(this.view); + this.view.webContents.focus(); + WindowManager.sendToRenderer(OPEN_TEAMS_DROPDOWN); + } + + handleClose = () => { + this.window.removeBrowserView(this.view); + WindowManager.sendToRenderer(CLOSE_TEAMS_DROPDOWN); + } + + handleReceivedMenuSize = (event: IpcMainEvent, width: number, height: number) => { + const bounds = this.getBounds(width, height); + this.view.setBounds(bounds); + } + + getBounds = (width: number, height: number) => { + return { + x: (process.platform === 'darwin' ? THREE_DOT_MENU_WIDTH_MAC : THREE_DOT_MENU_WIDTH) - MENU_SHADOW_WIDTH, + y: TAB_BAR_HEIGHT - MENU_SHADOW_WIDTH, + width, + height, + }; + } + + destroy = () => { + // workaround to eliminate zombie processes + // https://github.com/mattermost/desktop/pull/1519 + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + this.view.webContents.destroy(); + } +} diff --git a/src/main/views/viewManager.ts b/src/main/views/viewManager.ts index 21f9f06a..bf856b45 100644 --- a/src/main/views/viewManager.ts +++ b/src/main/views/viewManager.ts @@ -1,7 +1,7 @@ // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import log from 'electron-log'; -import {BrowserView, BrowserWindow, dialog} from 'electron'; +import {BrowserView, BrowserWindow, dialog, ipcMain} from 'electron'; import {BrowserViewConstructorOptions} from 'electron/main'; import {CombinedConfig, Team} from 'types/config'; @@ -131,6 +131,7 @@ export class ViewManager { return; } newView.window.webContents.send(SET_SERVER_KEY, serverInfo.order); + ipcMain.emit(SET_SERVER_KEY, true, name); if (newView.isReady()) { // if view is not ready, the renderer will have something to display instead. newView.show(); diff --git a/src/main/windows/windowManager.ts b/src/main/windows/windowManager.ts index e8c9f8db..923a368c 100644 --- a/src/main/windows/windowManager.ts +++ b/src/main/windows/windowManager.ts @@ -15,6 +15,8 @@ import {getAdjustedWindowBoundaries} from '../utils'; import {ViewManager} from '../views/viewManager'; import CriticalErrorHandler from '../CriticalErrorHandler'; +import TeamDropdownView from '../views/teamDropdownView'; + import {createSettingsWindow} from './settingsWindow'; import createMainWindow from './mainWindow'; @@ -25,6 +27,7 @@ type WindowManagerStatus = { settingsWindow?: BrowserWindow; config?: CombinedConfig; viewManager?: ViewManager; + teamDropdown?: TeamDropdownView; }; const status: WindowManagerStatus = {}; @@ -108,6 +111,8 @@ export function showMainWindow(deeplinkingURL?: string | URL) { if (status.viewManager) { status.viewManager.updateMainWindow(status.mainWindow); } + + status.teamDropdown = new TeamDropdownView(status.mainWindow, status.config.teams, status.config.darkMode); } initializeViewManager(); diff --git a/src/renderer/assets/fonts/compass-icons/compass-icons.eot b/src/renderer/assets/fonts/compass-icons/compass-icons.eot new file mode 100644 index 00000000..0c82f98a Binary files /dev/null and b/src/renderer/assets/fonts/compass-icons/compass-icons.eot differ diff --git a/src/renderer/assets/fonts/compass-icons/compass-icons.svg b/src/renderer/assets/fonts/compass-icons/compass-icons.svg new file mode 100644 index 00000000..869bc909 --- /dev/null +++ b/src/renderer/assets/fonts/compass-icons/compass-icons.svg @@ -0,0 +1,460 @@ + + + +Copyright (C) 2021 by original authors @ fontello.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/renderer/assets/fonts/compass-icons/compass-icons.ttf b/src/renderer/assets/fonts/compass-icons/compass-icons.ttf new file mode 100644 index 00000000..f2a5a90a Binary files /dev/null and b/src/renderer/assets/fonts/compass-icons/compass-icons.ttf differ diff --git a/src/renderer/assets/fonts/compass-icons/compass-icons.woff b/src/renderer/assets/fonts/compass-icons/compass-icons.woff new file mode 100644 index 00000000..77cd2544 Binary files /dev/null and b/src/renderer/assets/fonts/compass-icons/compass-icons.woff differ diff --git a/src/renderer/assets/fonts/compass-icons/compass-icons.woff2 b/src/renderer/assets/fonts/compass-icons/compass-icons.woff2 new file mode 100644 index 00000000..c466ff3a Binary files /dev/null and b/src/renderer/assets/fonts/compass-icons/compass-icons.woff2 differ diff --git a/src/renderer/components/MainPage.tsx b/src/renderer/components/MainPage.tsx index 6bd83efc..32e32140 100644 --- a/src/renderer/components/MainPage.tsx +++ b/src/renderer/components/MainPage.tsx @@ -36,6 +36,8 @@ import { ADD_SERVER, FOCUS_THREE_DOT_MENU, GET_FULL_SCREEN_STATUS, + CLOSE_TEAMS_DROPDOWN, + OPEN_TEAMS_DROPDOWN, } from 'common/communication'; import restoreButton from '../../assets/titlebar/chrome-restore.svg'; @@ -48,6 +50,7 @@ import {playSound} from '../notificationSounds'; import TabBar from './TabBar'; import ExtraBar from './ExtraBar'; import ErrorView from './ErrorView'; +import TeamDropdownButton from './TeamDropdownButton'; enum Status { LOADING = 1, @@ -78,6 +81,7 @@ type State = { modalOpen?: boolean; fullScreen?: boolean; showExtraBar?: boolean; + isMenuOpen: boolean; }; type TabStatus = { @@ -107,6 +111,7 @@ export default class MainPage extends React.PureComponent { maximized: false, tabStatus: new Map(this.props.teams.map((server) => [server.name, {status: Status.LOADING}])), darkMode: this.props.darkMode, + isMenuOpen: false, }; } @@ -228,6 +233,14 @@ export default class MainPage extends React.PureComponent { this.setState({unreadCounts: newUnreads, mentionCounts: newMentionCounts, sessionsExpired: expired}); }); + window.ipcRenderer.on(CLOSE_TEAMS_DROPDOWN, () => { + this.setState({isMenuOpen: false}); + }); + + window.ipcRenderer.on(OPEN_TEAMS_DROPDOWN, () => { + this.setState({isMenuOpen: true}); + }); + if (window.process.platform !== 'darwin') { window.ipcRenderer.on(FOCUS_THREE_DOT_MENU, () => { if (this.threeDotMenu.current) { @@ -305,6 +318,7 @@ export default class MainPage extends React.PureComponent { focusOnWebView = () => { window.ipcRenderer.send(FOCUS_BROWSERVIEW); + window.ipcRenderer.send(CLOSE_TEAMS_DROPDOWN); } render() { @@ -385,6 +399,8 @@ export default class MainPage extends React.PureComponent { ); } + const totalMentionCount = Object.values(this.state.mentionCounts).reduce((sum, value) => sum + value, 0); + const totalUnreadCount = Object.values(this.state.unreadCounts).reduce((sum, value) => sum + value, 0); const topRow = ( { > + 0} + isMenuOpen={this.state.isMenuOpen} + darkMode={this.state.darkMode} + /> {tabsRow} {overlayGradient} {titleBarButtons} diff --git a/src/renderer/components/TabBar.tsx b/src/renderer/components/TabBar.tsx index 33520f3e..bc5e2f04 100644 --- a/src/renderer/components/TabBar.tsx +++ b/src/renderer/components/TabBar.tsx @@ -165,6 +165,8 @@ export default class TabBar extends React.PureComponent { // need ); } + // TODO: Replace with products + tabs.length = 0; return ( = (props: Props) => { + const {activeServerName, totalMentionCount, hasUnreads, isMenuOpen, darkMode} = props; + + const handleToggleButton = (event: React.MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + window.ipcRenderer.send(isMenuOpen ? CLOSE_TEAMS_DROPDOWN : OPEN_TEAMS_DROPDOWN); + }; + + let badgeDiv: React.ReactNode; + if (totalMentionCount > 0) { + badgeDiv = ( +
+ {totalMentionCount > 99 ? '99+' : totalMentionCount} +
+ ); + } else if (hasUnreads) { + badgeDiv = ( +
+ ); + } + + return ( + + ); +}; + +export default TeamDropdownButton; diff --git a/src/renderer/css/compass-icons.css b/src/renderer/css/compass-icons.css new file mode 100755 index 00000000..08fc4d5a --- /dev/null +++ b/src/renderer/css/compass-icons.css @@ -0,0 +1,284 @@ +@charset "UTF-8"; + + @font-face { + font-family: 'compass-icons'; + src: url('../assets/fonts/compass-icons/compass-icons.eot?45182295'); + src: url('../assets/fonts/compass-icons/compass-icons.eot?45182295#iefix') format('embedded-opentype'), + url('../assets/fonts/compass-icons/compass-icons.woff2?45182295') format('woff2'), + url('../assets/fonts/compass-icons/compass-icons.woff?45182295') format('woff'), + url('../assets/fonts/compass-icons/compass-icons.ttf?45182295') format('truetype'), + url('../assets/fonts/compass-icons/compass-icons.svg?45182295#compass-icons') format('svg'); + font-weight: normal; + font-style: normal; +} +/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ +/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ +/* +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: 'compass-icons'; + src: url('../fonts/compass-icons/compass-icons.svg?45182295#compass-icons') format('svg'); + } +} +*/ + +[class^="icon-"]:before, [class*=" icon-"]:before { + font-family: "compass-icons"; + font-style: normal; + font-weight: normal; + speak: never; + + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + margin-left: .2em; + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Font smoothing. That was taken from TWBS */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} + +.icon-mattermost:before { content: '\e800'; } /* '' */ +.icon-archive-outline:before { content: '\e801'; } /* '' */ +.icon-beach-umbrella-outline:before { content: '\e802'; } /* '' */ +.icon-exclamation-thick:before { content: '\e803'; } /* '' */ +.icon-gfycat:before { content: '\e804'; } /* '' */ +.icon-globe:before { content: '\e805'; } /* '' */ +.icon-iframe-list-outline:before { content: '\e806'; } /* '' */ +.icon-oauth:before { content: '\e807'; } /* '' */ +.icon-power-plug-outline:before { content: '\e808'; } /* '' */ +.icon-robot-happy:before { content: '\e809'; } /* '' */ +.icon-server-outline:before { content: '\e80a'; } /* '' */ +.icon-slash-forward-box-outline:before { content: '\e80b'; } /* '' */ +.icon-webhook-incoming:before { content: '\e80c'; } /* '' */ +.icon-webhook-outgoing:before { content: '\e80d'; } /* '' */ +.icon-image-area-outline:before { content: '\e80e'; } /* '' */ +.icon-emoticon-plus-outline:before { content: '\e80f'; } /* '' */ +.icon-mark-as-unread:before { content: '\e810'; } /* '' */ +.icon-arrow-back-ios:before { content: '\e811'; } /* '' */ +.icon-arrow-forward-ios:before { content: '\e812'; } /* '' */ +.icon-brand-zoom:before { content: '\e813'; } /* '' */ +.icon-menu-variant:before { content: '\e814'; } /* '' */ +.icon-message-check:before { content: '\e815'; } /* '' */ +.icon-airplane-variant:before { content: '\e816'; } /* '' */ +.icon-food-apple:before { content: '\e817'; } /* '' */ +.icon-leaf-outline:before { content: '\e818'; } /* '' */ +.icon-basketball:before { content: '\e819'; } /* '' */ +.icon-emoticon-custom-outline:before { content: '\e81a'; } /* '' */ +.icon-message-check-outline:before { content: '\e81b'; } /* '' */ +.icon-image-broken-outline:before { content: '\e81c'; } /* '' */ +.icon-format-header:before { content: '\e81d'; } /* '' */ +.icon-circle-multiple-outline-lock:before { content: '\e81e'; } /* '' */ +.icon-server-variant:before { content: '\e81f'; } /* '' */ +.icon-server-variant-plus:before { content: '\e820'; } /* '' */ +.icon-search-list:before { content: '\e821'; } /* '' */ +.icon-brand-gitlab:before { content: '\e822'; } /* '' */ +.icon-brand-google:before { content: '\e823'; } /* '' */ +.icon-brand-office-365:before { content: '\e824'; } /* '' */ +.icon-brand-one-login:before { content: '\e825'; } /* '' */ +.icon-application-cog:before { content: '\e826'; } /* '' */ +.icon-key-variant-circle:before { content: '\e827'; } /* '' */ +.icon-car-outline:before { content: '\e899'; } /* '' */ +.icon-jumbo-attachment-text:before { content: '\e900'; } /* '' */ +.icon-jumbo-attachment-word:before { content: '\e901'; } /* '' */ +.icon-jumbo-attachment-excel:before { content: '\e902'; } /* '' */ +.icon-jumbo-attachment-powerpoint:before { content: '\e903'; } /* '' */ +.icon-jumbo-attachment-pdf:before { content: '\e904'; } /* '' */ +.icon-jumbo-attachment-image:before { content: '\e905'; } /* '' */ +.icon-jumbo-attachment-video:before { content: '\e906'; } /* '' */ +.icon-jumbo-attachment-audio:before { content: '\e907'; } /* '' */ +.icon-jumbo-attachment-generic:before { content: '\e908'; } /* '' */ +.icon-jumbo-attachment-patch:before { content: '\e909'; } /* '' */ +.icon-jumbo-attachment-zip:before { content: '\e90a'; } /* '' */ +.icon-jumbo-attachment-code:before { content: '\e90b'; } /* '' */ +.icon-jumbo-attachment-image-broken:before { content: '\e90c'; } /* '' */ +.icon-account-multiple-outline:before { content: '\f00f'; } /* '' */ +.icon-airplane:before { content: '\f01d'; } /* '' */ +.icon-alert-outline:before { content: '\f02a'; } /* '' */ +.icon-arrow-down:before { content: '\f045'; } /* '' */ +.icon-arrow-left:before { content: '\f04d'; } /* '' */ +.icon-arrow-up:before { content: '\f05d'; } /* '' */ +.icon-at:before { content: '\f065'; } /* '' */ +.icon-flask-outline:before { content: '\f096'; } /* '' */ +.icon-bell-outline:before { content: '\f09c'; } /* '' */ +.icon-cellphone:before { content: '\f11c'; } /* '' */ +.icon-chart-bar:before { content: '\f128'; } /* '' */ +.icon-chart-line:before { content: '\f12a'; } /* '' */ +.icon-check:before { content: '\f12c'; } /* '' */ +.icon-checkbox-blank-outline:before { content: '\f131'; } /* '' */ +.icon-checkbox-marked:before { content: '\f132'; } /* '' */ +.icon-chevron-down:before { content: '\f140'; } /* '' */ +.icon-chevron-left:before { content: '\f141'; } /* '' */ +.icon-chevron-right:before { content: '\f142'; } /* '' */ +.icon-clock-outline:before { content: '\f150'; } /* '' */ +.icon-close:before { content: '\f156'; } /* '' */ +.icon-close-circle-outline:before { content: '\f15a'; } /* '' */ +.icon-code-brackets:before { content: '\f16a'; } /* '' */ +.icon-code-tags:before { content: '\f174'; } /* '' */ +.icon-console:before { content: '\f18d'; } /* '' */ +.icon-dots-horizontal:before { content: '\f1d8'; } /* '' */ +.icon-dots-vertical:before { content: '\f1d9'; } /* '' */ +.icon-email-outline:before { content: '\f1f0'; } /* '' */ +.icon-emoticon-outline:before { content: '\f1f2'; } /* '' */ +.icon-emoticon-happy-outline:before { content: '\f1f5'; } /* '' */ +.icon-filter-variant:before { content: '\f236'; } /* '' */ +.icon-fire:before { content: '\f238'; } /* '' */ +.icon-flag:before { content: '\f23b'; } /* '' */ +.icon-flag-outline:before { content: '\f23d'; } /* '' */ +.icon-format-bold:before { content: '\f264'; } /* '' */ +.icon-format-clear:before { content: '\f265'; } /* '' */ +.icon-format-header-1:before { content: '\f26b'; } /* '' */ +.icon-format-header-2:before { content: '\f26c'; } /* '' */ +.icon-format-header-3:before { content: '\f26d'; } /* '' */ +.icon-format-header-4:before { content: '\f26e'; } /* '' */ +.icon-format-header-5:before { content: '\f26f'; } /* '' */ +.icon-format-header-6:before { content: '\f270'; } /* '' */ +.icon-format-italic:before { content: '\f277'; } /* '' */ +.icon-format-list-bulleted:before { content: '\f279'; } /* '' */ +.icon-format-list-numbered:before { content: '\f27b'; } /* '' */ +.icon-format-strikethrough-variant:before { content: '\f281'; } /* '' */ +.icon-github-circle:before { content: '\f2a4'; } /* '' */ +.icon-heart-outline:before { content: '\f2d5'; } /* '' */ +.icon-help:before { content: '\f2d6'; } /* '' */ +.icon-information-outline:before { content: '\f2fd'; } /* '' */ +.icon-laptop:before { content: '\f322'; } /* '' */ +.icon-leaf:before { content: '\f32a'; } /* '' */ +.icon-lightbulb-outline:before { content: '\f336'; } /* '' */ +.icon-link-variant:before { content: '\f339'; } /* '' */ +.icon-link-variant-off:before { content: '\f33a'; } /* '' */ +.icon-lock:before { content: '\f33e'; } /* '' */ +.icon-lock-outline:before { content: '\f341'; } /* '' */ +.icon-magnify:before { content: '\f349'; } /* '' */ +.icon-menu:before { content: '\f35c'; } /* '' */ +.icon-menu-down:before { content: '\f35d'; } /* '' */ +.icon-minus-box:before { content: '\f375'; } /* '' */ +.icon-minus-circle:before { content: '\f376'; } /* '' */ +.icon-paperclip:before { content: '\f3e2'; } /* '' */ +.icon-pin:before { content: '\f403'; } /* '' */ +.icon-plus:before { content: '\f415'; } /* '' */ +.icon-plus-box:before { content: '\f416'; } /* '' */ +.icon-refresh:before { content: '\f450'; } /* '' */ +.icon-send:before { content: '\f48a'; } /* '' */ +.icon-shield-outline:before { content: '\f499'; } /* '' */ +.icon-sitemap:before { content: '\f4aa'; } /* '' */ +.icon-soccer:before { content: '\f4b8'; } /* '' */ +.icon-source-pull:before { content: '\f4c2'; } /* '' */ +.icon-star:before { content: '\f4ce'; } /* '' */ +.icon-star-outline:before { content: '\f4d2'; } /* '' */ +.icon-sync:before { content: '\f4e6'; } /* '' */ +.icon-alert-circle-outline:before { content: '\f5d6'; } /* '' */ +.icon-check-circle:before { content: '\f5e0'; } /* '' */ +.icon-email-variant:before { content: '\f5f0'; } /* '' */ +.icon-food-fork-drink:before { content: '\f5f2'; } /* '' */ +.icon-arrow-collapse:before { content: '\f615'; } /* '' */ +.icon-arrow-expand:before { content: '\f616'; } /* '' */ +.icon-source-branch:before { content: '\f62c'; } /* '' */ +.icon-tune:before { content: '\f62e'; } /* '' */ +.icon-webhook:before { content: '\f62f'; } /* '' */ +.icon-eye-outline:before { content: '\f6cf'; } /* '' */ +.icon-cancel:before { content: '\f739'; } /* '' */ +.icon-format-quote-open:before { content: '\f756'; } /* '' */ +.icon-square:before { content: '\f763'; } /* '' */ +.icon-circle-outline:before { content: '\f765'; } /* '' */ +.icon-account-plus-outline:before { content: '\f800'; } /* '' */ +.icon-forum-outline:before { content: '\f821'; } /* '' */ +.icon-settings-outline:before { content: '\f8ba'; } /* '' */ +.icon-hammer:before { content: '\f8e9'; } /* '' */ +.icon-pin-outline:before { content: '\f930'; } /* '擄' */ +.icon-clock:before { content: '\f953'; } /* '肋' */ +.icon-image-outline:before { content: '\f975'; } /* '掠' */ +.icon-email-plus-outline:before { content: '\f9eb'; } /* '匿' */ +.icon-file-document-outline:before { content: '\f9ed'; } /* '吝' */ +.icon-layers-outline:before { content: '\f9fd'; } /* '什' */ +.icon-bell-off-outline:before { content: '\fa90'; } /* '敖' */ +.icon-chevron-down-circle-outline:before { content: '\fb0c'; } /* '﬌' */ +.icon-format-letter-case:before { content: '\fb19'; } /* '﬙' */ +.icon-download-outline:before { content: '\fb6b'; } /* 'ﭫ' */ +.icon-video-outline:before { content: '\fbb8'; } /* '﮸' */ +.icon-pencil-outline:before { content: '\fc92'; } /* 'ﲒ' */ +.icon-camera-outline:before { content: '\fd39'; } /* 'ﴹ' */ +.icon-file-video-outline:before { content: '\fe10'; } /* '︐' */ +.icon-palette-outline:before { content: '\fe6c'; } /* '﹬' */ +.icon-file-music-outline:before { content: '\fe7c'; } /* 'ﹼ' */ +.icon-file-pdf-outline:before { content: '\fe7d'; } /* 'ﹽ' */ +.icon-file-image-outline:before { content: '\fecd'; } /* 'ﻍ' */ +.icon-reply-outline:before { content: '\ff3d'; } /* ']' */ +.icon-draw:before { content: '\ff66'; } /* 'ヲ' */ +.icon-account-outline:before { content: '󰀓'; } /* '\f0013' */ +.icon-arrow-expand-all:before { content: '󰁌'; } /* '\f004c' */ +.icon-file-code-outline:before { content: '󰁍'; } /* '\f004d' */ +.icon-file-excel-outline:before { content: '󰁏'; } /* '\f004f' */ +.icon-arrow-right:before { content: '󰁔'; } /* '\f0054' */ +.icon-file-powerpoint-outline:before { content: '󰁗'; } /* '\f0057' */ +.icon-file-word-outline:before { content: '󰁠'; } /* '\f0060' */ +.icon-bookmark:before { content: '󰃀'; } /* '\f00c0' */ +.icon-bookmark-outline:before { content: '󰃃'; } /* '\f00c3' */ +.icon-dock-left:before { content: '󰃕'; } /* '\f00d5' */ +.icon-chevron-up:before { content: '󰅃'; } /* '\f0143' */ +.icon-close-circle:before { content: '󰅙'; } /* '\f0159' */ +.icon-content-copy:before { content: '󰆏'; } /* '\f018f' */ +.icon-send-outline:before { content: '󰆐'; } /* '\f0190' */ +.icon-credit-card-outline:before { content: '󰆛'; } /* '\f019b' */ +.icon-view-grid-outline:before { content: '󰈄'; } /* '\f0204' */ +.icon-exit-to-app:before { content: '󰈆'; } /* '\f0206' */ +.icon-file-outline:before { content: '󰈤'; } /* '\f0224' */ +.icon-folder-outline:before { content: '󰉖'; } /* '\f0256' */ +.icon-archive-arrow-up-outline:before { content: '󰊇'; } /* '\f0287' */ +.icon-glasses:before { content: '󰊪'; } /* '\f02aa' */ +.icon-key-variant:before { content: '󰌋'; } /* '\f030b' */ +.icon-magnify-minus:before { content: '󰍊'; } /* '\f034a' */ +.icon-magnify-plus:before { content: '󰍋'; } /* '\f034b' */ +.icon-menu-left:before { content: '󰍞'; } /* '\f035e' */ +.icon-menu-right:before { content: '󰍟'; } /* '\f035f' */ +.icon-menu-up:before { content: '󰍠'; } /* '\f0360' */ +.icon-message-text-outline:before { content: '󰍪'; } /* '\f036a' */ +.icon-minus:before { content: '󰍴'; } /* '\f0374' */ +.icon-minus-circle-outline:before { content: '󰍷'; } /* '\f0377' */ +.icon-pause:before { content: '󰏤'; } /* '\f03e4' */ +.icon-play:before { content: '󰐊'; } /* '\f040a' */ +.icon-radiobox-blank:before { content: '󰐽'; } /* '\f043d' */ +.icon-radiobox-marked:before { content: '󰐾'; } /* '\f043e' */ +.icon-sort-alphabetical-ascending:before { content: '󰖽'; } /* '\f05bd' */ +.icon-playlist-check:before { content: '󰗇'; } /* '\f05c7' */ +.icon-check-circle-outline:before { content: '󰗡'; } /* '\f05e1' */ +.icon-help-circle-outline:before { content: '󰘥'; } /* '\f0625' */ +.icon-circle-multiple-outline:before { content: '󰚕'; } /* '\f0695' */ +.icon-update:before { content: '󰚰'; } /* '\f06b0' */ +.icon-eye-off-outline:before { content: '󰛑'; } /* '\f06d1' */ +.icon-infinity:before { content: '󰛤'; } /* '\f06e4' */ +.icon-arrow-right-bold-outline:before { content: '󰧂'; } /* '\f09c2' */ +.icon-trash-can-outline:before { content: '󰩺'; } /* '\f0a7a' */ +.icon-account-minus-outline:before { content: '󰫬'; } /* '\f0aec' */ +.icon-calendar-outline:before { content: '󰭧'; } /* '\f0b67' */ +.icon-export-variant:before { content: '󰮓'; } /* '\f0b93' */ +.icon-folder-plus-outline:before { content: '󰮝'; } /* '\f0b9d' */ +.icon-home-variant-outline:before { content: '󰮧'; } /* '\f0ba7' */ +.icon-calendar-check-outline:before { content: '󰱄'; } /* '\f0c44' */ +.icon-hand-right:before { content: '󰹇'; } /* '\f0e47' */ +.icon-notebook-outline:before { content: '󰺿'; } /* '\f0ebf' */ +.icon-shield-alert-outline:before { content: '󰻍'; } /* '\f0ecd' */ +.icon-slash-forward:before { content: '󰿟'; } /* '\f0fdf' */ +.icon-file-multiple-outline:before { content: '󱀲'; } /* '\f1032' */ +.icon-message-plus-outline:before { content: '󱂻'; } /* '\f10bb' */ +.icon-message-minus-outline:before { content: '󱅯'; } /* '\f116f' */ +.icon-crown-outline:before { content: '󱇐'; } /* '\f11d0' */ +.icon-folder-move-outline:before { content: '󱉆'; } /* '\f1246' */ diff --git a/src/renderer/css/components/TabBar.css b/src/renderer/css/components/TabBar.css index 46b4f364..131655a2 100644 --- a/src/renderer/css/components/TabBar.css +++ b/src/renderer/css/components/TabBar.css @@ -9,10 +9,6 @@ margin-top: 4px; } -.TabBar.darkMode { - background-color: #202124; -} - .TabBar .teamTabItem span { flex: 0 1 auto; overflow: hidden; diff --git a/src/renderer/css/components/TeamDropdownButton.scss b/src/renderer/css/components/TeamDropdownButton.scss new file mode 100644 index 00000000..d365650d --- /dev/null +++ b/src/renderer/css/components/TeamDropdownButton.scss @@ -0,0 +1,116 @@ +.TeamDropdownButton { + background-color: transparent; + border-width: 0 1px; + border-color: rgba(61, 60, 64, 0.08); + border-style: solid; + display: flex; + align-items: center; + font-family: Open Sans; + + &:hover { + background-color: #f4f4f4; + + .TeamDropdownButton__badge-count, .TeamDropdownButton__badge-unreads { + border-color: #f4f4f4; + } + } + + &:focus, &.isMenuOpen { + background-color: #fff; + + .TeamDropdownButton__badge-count, .TeamDropdownButton__badge-unreads { + border-color: #fff; + } + } + + > span { + color: rgba(61, 60, 64, 0.64); + font-weight: 600; + font-size: 12px; + line-height: 16px; + margin-left: 8px; + } + + i { + color: rgba(61, 60, 64, 0.56); + font-size: 18px; + line-height: 21px; + } +} + +.TeamDropdownButton__badge { + position: relative; +} + +.TeamDropdownButton__badge-count { + background: #F74343; + border-radius: 8px; + display: flex; + align-items: center; + flex: 0 0 auto; + position: absolute; + top: -4px; + right: -2px; + border: 2px solid #efefef; + + > span { + color: white; + font-size: 10px; + line-height: 11px; + padding: 1px 5px; + display: flex; + justify-content: center; + align-items: center; + font-family: "Open Sans", sans-serif; + font-weight: bold; + letter-spacing: normal; + -webkit-font-smoothing: antialiased; + } +} + +.TeamDropdownButton__badge-unreads { + background: #579eff; + border-radius: 100px; + width: 12px; + height: 12px; + position: absolute; + top: -4px; + right: -2px; + border: 2px solid #efefef; +} + +.TeamDropdownButton.darkMode { + border-color: rgba(221, 221, 221, 0.08); + + &:hover { + background-color: #292929; + + .TeamDropdownButton__badge-count, .TeamDropdownButton__badge-unreads { + border-color: #292929; + } + } + + &:focus, &.isMenuOpen { + background-color: #1f1f1f; + + .TeamDropdownButton__badge-count, .TeamDropdownButton__badge-unreads { + border-color: #1f1f1f; + } + } + + > span { + color: rgba(221, 221, 221, 0.64); + } + + i { + color: rgba(221, 221, 221, 0.56); + } + + .TeamDropdownButton__badge-count { + border-color: #2e2e2e; + } + + .TeamDropdownButton__badge-unreads { + border-color: #2e2e2e; + } +} diff --git a/src/renderer/css/dropdown.scss b/src/renderer/css/dropdown.scss new file mode 100644 index 00000000..d4a4904c --- /dev/null +++ b/src/renderer/css/dropdown.scss @@ -0,0 +1,198 @@ +@import url("fonts.css"); + +body { + margin: 0; + background-color: transparent; + font-family: Open Sans; + overflow: hidden; +} + +#app { + padding: 24px; + display: inline-block; +} + +.TeamDropdown { + display: flex; + flex-direction: column; + align-items: flex-start; + padding: 8px 0px; + background: #FFFFFF; + border: 1px solid rgba(61, 60, 64, 0.16); + box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.12); + border-radius: 4px; + min-width: 354px; +} + +.TeamDropdown__header { + padding: 6px 20px; + + > span { + font-weight: 600; + font-size: 14px; + line-height: 20px; + color: #3D3C40; + } +} + +.TeamDropdown__divider { + border-top: 1px solid rgba(61, 60, 64, 0.08); + border-bottom: 0; + width: 100%; + margin: 8px 0; +} + +.TeamDropdown__button { + background-color: transparent; + border: none; + padding: 8px 18px; + display: flex; + width: 100%; + align-items: center; + font-family: Open Sans; + + &:hover { + background-color: rgba(61, 60, 64, 0.08); + + .TeamDropdown__button-edit, .TeamDropdown__button-remove { + opacity: 1; + pointer-events: all; + } + } + + &:focus, &:focus-within { + background-color: rgba(22, 109, 224, 0.08); + outline: none; + + .TeamDropdown__button-edit, .TeamDropdown__button-remove { + opacity: 1; + pointer-events: all; + } + } + + i { + font-size: 18px; + line-height: 20px; + color: rgba(61, 60, 64, 0.56); + } + + > i.icon-check { + color: #166de0; + } + + > span { + font-size: 14px; + line-height: 20px; + color: #3D3C40; + margin-left: 12px; + white-space: nowrap; + } +} + +.TeamDropdown__badge { + display: flex; + justify-content: center; + align-items: center; + margin-left: 18px; + min-width: 32px; +} + +.TeamDropdown__badge-dot { + background: #579EFF; + height: 8px; + width: 8px; + border-radius: 8px; + flex: 0 0 8px; +} + +.TeamDropdown__badge-count { + background: #F74343; + text-align: center; + border-radius: 8px; + padding: 0 4px; + display: flex; + align-items: center; + justify-content: center; + + > span { + letter-spacing: 0.02em; + -webkit-font-smoothing: antialiased; + font-family: "Open Sans", sans-serif; + line-height: 16px; + padding: 0 2.5px; + color: white; + font-size: 11px; + font-weight: bold; + } +} + +.TeamDropdown__indicators { + margin-left: auto; + display: flex; + + > button { + margin-left: 18px; + background-color: transparent; + border: none; + padding: 0; + + > i::before { + margin: 0; + } + } +} + +.TeamDropdown__button-edit, .TeamDropdown__button-remove { + opacity: 0; + pointer-events: none; +} + +.TeamDropdown__button-remove { + margin-right: 7px; + + + .TeamDropdown__badge { + margin-left: 7px; + } + + i { + color: #f74343; + } +} + +.TeamDropdown.darkMode { + background: #1f1f1f; + + .TeamDropdown__header > span { + color: #DDD; + } + + .TeamDropdown__divider { + border-color: rgba(221, 221, 221, 0.08); + } + + .TeamDropdown__button { + &:hover { + background-color: rgba(221, 221, 221, 0.08); + } + + &:focus { + background-color: rgba(1, 119, 231, 0.08); + } + + > i.icon-server-variant, i.icon-plus { + color: rgba(221, 221, 221, 0.56); + } + + > span { + color: #DDD; + } + } + + .TeamDropdown__badge-expired i { + color: rgba(221, 221, 221, 0.56); + } + + .TeamDropdown__button-edit > i { + color: rgba(221, 221, 221, 0.56); + } +} diff --git a/src/renderer/css/fonts.css b/src/renderer/css/fonts.css new file mode 100644 index 00000000..1feac8dc --- /dev/null +++ b/src/renderer/css/fonts.css @@ -0,0 +1,41 @@ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-300.woff2') format('woff2'); + } + + @font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-300italic.woff2') format('woff2'); + } + + @font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-regular.woff2') format('woff2'); + } + + @font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-italic.woff2') format('woff2'); + } + + @font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-600.woff2') format('woff2'); + } + + @font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-600italic.woff2') format('woff2'); + } diff --git a/src/renderer/css/index.css b/src/renderer/css/index.css index 08a2d9ea..5515aa67 100644 --- a/src/renderer/css/index.css +++ b/src/renderer/css/index.css @@ -1,47 +1,7 @@ @import url("components/index.css"); +@import url("fonts.css"); @import '~font-awesome/css/font-awesome.css'; -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-300.woff2') format('woff2'); -} - -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 300; - src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-300italic.woff2') format('woff2'); -} - -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-regular.woff2') format('woff2'); -} - -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-italic.woff2') format('woff2'); -} - -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 600; - src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-600.woff2') format('woff2'); -} - -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 600; - src: url('../../assets/fonts/open-sans-v13-latin-ext_latin_cyrillic-ext_greek-ext_greek_cyrillic_vietnamese-600italic.woff2') format('woff2'); -} html { height: 100%; @@ -85,8 +45,8 @@ body { .topBar>.topBar-bg { display: flex; overflow: hidden; - height: 36px; - background-color: rgba(0,0,0,0.1); + height: 40px; + background-color: #efefef; width: 100%; } @@ -94,13 +54,8 @@ body { opacity: 0.4; } -.topBar.darkMode { - background-color: #323639; - color: #fff; -} - .topBar.darkMode>.topBar-bg { - background-color: #202124; + background-color: #2e2e2e; } .topBar .three-dot-menu { @@ -114,7 +69,7 @@ body { z-index: 9; color: rgba(61,60,64,0.7); -webkit-app-region: no-drag; - background-color: rgba(229, 229, 229, 1); + background-color: transparent; } .topBar .three-dot-menu svg { @@ -155,13 +110,11 @@ body { } .topBar.darkMode .three-dot-menu { - background-color: #202124; color: rgba(243,243,243,0.7); } .topBar.darkMode .title-bar-btns { color: rgba(243,243,243,0.7); - background-color: #202124; } .topBar .title-bar-btns { @@ -173,7 +126,6 @@ body { -webkit-app-region: no-drag; display: grid; grid-template-columns: repeat(3, 46px); - background-color: #e5e5e5; } .topBar .title-bar-btns>.button { @@ -244,11 +196,11 @@ body { .topBar .overlay-gradient { flex: 0 0 40px; z-index: 9; - background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, #e5e5e5 100%); + /* background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, #e5e5e5 100%); */ -webkit-app-region: drag; margin-top: 4px; } .topBar.darkMode .overlay-gradient { - background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, #202124 100%); + /* background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, #202124 100%); */ } diff --git a/src/renderer/dropdown.tsx b/src/renderer/dropdown.tsx new file mode 100644 index 00000000..4ac39068 --- /dev/null +++ b/src/renderer/dropdown.tsx @@ -0,0 +1,166 @@ +// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import ReactDOM from 'react-dom'; +import classNames from 'classnames'; + +import {Team} from 'types/config'; + +import {CLOSE_TEAMS_DROPDOWN, REQUEST_TEAMS_DROPDOWN_INFO, SEND_DROPDOWN_MENU_SIZE, SHOW_NEW_SERVER_MODAL, SWITCH_SERVER, UPDATE_TEAMS_DROPDOWN} from 'common/communication'; + +import './css/dropdown.scss'; +import './css/compass-icons.css'; + +type State = { + teams?: Team[]; + orderedTeams?: Team[]; + activeTeam?: string; + darkMode?: boolean; + unreads?: Map; + mentions?: Map; + expired?: Map; +} + +class TeamDropdown extends React.PureComponent, State> { + constructor(props: Record) { + super(props); + this.state = {}; + + window.addEventListener('message', this.handleMessageEvent); + } + + handleMessageEvent = (event: MessageEvent) => { + if (event.data.type === UPDATE_TEAMS_DROPDOWN) { + const {teams, activeTeam, darkMode, unreads, mentions, expired} = event.data.data; + this.setState({ + teams, + orderedTeams: teams.concat().sort((a: Team, b: Team) => a.order - b.order), + activeTeam, + darkMode, + unreads, + mentions, + expired, + }); + } + } + + selectServer = (team: Team) => { + return () => { + window.postMessage({type: SWITCH_SERVER, data: team.name}, window.location.href); + this.closeMenu(); + }; + } + + closeMenu = () => { + (document.activeElement as HTMLElement).blur(); + window.postMessage({type: CLOSE_TEAMS_DROPDOWN}, window.location.href); + } + + preventPropogation = (event: React.MouseEvent) => { + event.stopPropagation(); + } + + addServer = () => { + window.postMessage({type: SHOW_NEW_SERVER_MODAL}, window.location.href); + this.closeMenu(); + } + + isActiveTeam = (team: Team) => { + return team.name === this.state.activeTeam; + } + + componentDidMount() { + window.postMessage({type: REQUEST_TEAMS_DROPDOWN_INFO}, window.location.href); + window.addEventListener('click', this.closeMenu); + } + + componentDidUpdate() { + window.postMessage({type: SEND_DROPDOWN_MENU_SIZE, data: {width: document.body.scrollWidth, height: document.body.scrollHeight}}, window.location.href); + } + + componentWillUnmount() { + window.removeEventListener('click', this.closeMenu); + } + + render() { + return ( +
+
+ {'Servers'} +
+
+ {this.state.orderedTeams?.map((team, index) => { + const sessionExpired = this.state.expired?.get(team.name); + const hasUnreads = this.state.unreads?.get(team.name); + const mentionCount = this.state.mentions?.get(team.name); + + let badgeDiv: React.ReactNode; + if (sessionExpired) { + badgeDiv = ( +
+ +
+ ); + } else if (mentionCount && mentionCount > 0) { + badgeDiv = ( +
+ {mentionCount > 99 ? '99+' : mentionCount} +
+ ); + } else if (hasUnreads) { + badgeDiv = ( +
+ ); + } + + return ( + + + {badgeDiv &&
+ {badgeDiv} +
} +
+ + ); + })} +
+ +
+ ); + } +} + +ReactDOM.render( + , + document.getElementById('app'), +); diff --git a/webpack.config.main.js b/webpack.config.main.js index 7803cd0c..efd2f53f 100644 --- a/webpack.config.main.js +++ b/webpack.config.main.js @@ -18,6 +18,7 @@ module.exports = merge(base, { entry: { index: './src/main/main.ts', mainWindow: './src/main/preload/mainWindow.js', + dropdown: './src/main/preload/dropdown.js', preload: './src/main/preload/mattermost.js', modalPreload: './src/main/preload/modalPreload.js', loadingScreenPreload: './src/main/preload/loadingScreenPreload.js', diff --git a/webpack.config.renderer.js b/webpack.config.renderer.js index 9e0a592d..83b8863d 100644 --- a/webpack.config.renderer.js +++ b/webpack.config.renderer.js @@ -19,6 +19,7 @@ module.exports = merge(base, { entry: { index: './src/renderer/index.tsx', settings: './src/renderer/settings.tsx', + dropdown: './src/renderer/dropdown.tsx', urlView: './src/renderer/modals/urlView/urlView.tsx', newServer: './src/renderer/modals/newServer/newServer.tsx', loginModal: './src/renderer/modals/login/login.tsx', @@ -43,6 +44,12 @@ module.exports = merge(base, { chunks: ['settings'], filename: 'settings.html', }), + new HtmlWebpackPlugin({ + title: 'Mattermost Desktop Settings', + template: 'src/renderer/index.html', + chunks: ['dropdown'], + filename: 'dropdown.html', + }), new HtmlWebpackPlugin({ title: 'Mattermost Desktop Settings', template: 'src/renderer/index.html', @@ -97,6 +104,13 @@ module.exports = merge(base, { MiniCssExtractPlugin.loader, 'css-loader', ], + }, { + test: /\.scss$/, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + 'sass-loader', + ], }, { test: /\.mp3$/, use: {