From de8d542fa775ede064001633575283e618314c28 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 23 Oct 2024 00:01:28 +0300 Subject: [PATCH 01/34] add fontawesome and misc loader packages --- package-lock.json | 626 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 21 +- 2 files changed, 639 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index bd4c65f..f76b269 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,14 +29,17 @@ "@babel/preset-react": "^7.16.7", "@babel/register": "^7.17.0", "@babel/runtime": "^7.17.8", + "@fortawesome/fontawesome-free": ">=6.6.0", "babel-jest": "^27.5.1", "babel-loader": "^8.2.3", "babel-plugin-minify-dead-code-elimination": "^0.5.1", + "css-loader": "^3.6.0", "d3": ">=5.9.0", "eslint": "^8.10.0", "eslint-plugin-cypress": "^2.12.1", "eslint-plugin-react": "^7.29.2", "fancy-log": "^1.3.3", + "file-loader": "^4.3.0", "gulp": "^4.0.0", "http-server": "^0.12.1", "jasmine": "^3.5.0", @@ -46,9 +49,12 @@ "react-dom": ">=16.14.0", "react-transition-group": "^4.4.1", "sass": "^1.39.0", + "sass-loader": "^7.3.1", "source-map-support": "^0.5.19", "string-replace-loader": "^2.3.0", + "style-loader": "^1.3.0", "terser-webpack-plugin": "^4.2.3", + "url-loader": "^2.3.0", "webpack": "^4.46.0" }, "peerDependencies": { @@ -1975,6 +1981,15 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", + "integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -4412,6 +4427,75 @@ "node": "*" } }, + "node_modules/css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/css-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/css-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", @@ -5987,6 +6071,48 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz", + "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "dev": true, + "dependencies": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "peerDependencies": { + "webpack": "^4.0.0" + } + }, + "node_modules/file-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/file-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -7181,6 +7307,18 @@ "node": ">=0.10.0" } }, + "node_modules/icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "dependencies": { + "postcss": "^7.0.14" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -9599,6 +9737,107 @@ "node": ">=0.10.0" } }, + "node_modules/postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "dependencies": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "dev": true, + "dependencies": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dev": true, + "dependencies": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dev": true, + "dependencies": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/postcss/node_modules/picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "node_modules/postcss/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -10260,6 +10499,51 @@ "node": ">=12.0.0" } }, + "node_modules/sass-loader": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", + "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.0.1", + "neo-async": "^2.5.0", + "pify": "^4.0.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "webpack": "^3.0.0 || ^4.0.0" + } + }, + "node_modules/sass-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/sass-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/scheduler": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", @@ -11083,6 +11367,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-loader": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", + "integrity": "sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^2.7.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -11825,6 +12129,67 @@ "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", "dev": true }, + "node_modules/url-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-2.3.0.tgz", + "integrity": "sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog==", + "dev": true, + "dependencies": { + "loader-utils": "^1.2.3", + "mime": "^2.4.4", + "schema-utils": "^2.5.0" + }, + "engines": { + "node": ">= 8.9.0" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, + "node_modules/url-loader/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/url-loader/node_modules/loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/url-loader/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/url/node_modules/punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", @@ -14200,6 +14565,12 @@ } } }, + "@fortawesome/fontawesome-free": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.6.0.tgz", + "integrity": "sha512-60G28ke/sXdtS9KZCpZSHHkCbdsOGEhIUGlwq6yhY74UpTiToIh8np7A8yphhM4BWsvNFtIvLpi4co+h9Mr9Ow==", + "dev": true + }, "@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -16189,6 +16560,55 @@ "randomfill": "^1.0.3" } }, + "css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" + }, + "dependencies": { + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, "csstype": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz", @@ -17436,6 +17856,38 @@ "flat-cache": "^3.0.4" } }, + "file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-4.3.0.tgz", + "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + }, + "dependencies": { + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -18382,6 +18834,15 @@ "safer-buffer": ">= 2.1.2 < 3.0.0" } }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + } + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -20273,6 +20734,87 @@ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", "dev": true }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "dev": true, + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dev": true, + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dev": true, + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -20808,6 +21350,41 @@ "source-map-js": ">=0.6.2 <2.0.0" } }, + "sass-loader": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", + "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.0.1", + "neo-async": "^2.5.0", + "pify": "^4.0.1", + "semver": "^6.3.0" + }, + "dependencies": { + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + } + } + }, "scheduler": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", @@ -21487,6 +22064,16 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "style-loader": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", + "integrity": "sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^2.7.0" + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -22067,6 +22654,45 @@ "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", "dev": true }, + "url-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-2.3.0.tgz", + "integrity": "sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "mime": "^2.4.4", + "schema-utils": "^2.5.0" + }, + "dependencies": { + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + } + } + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", diff --git a/package.json b/package.json index fa6bb62..f1efe2d 100644 --- a/package.json +++ b/package.json @@ -27,42 +27,47 @@ "author": "4DN DCIC", "license": "MIT", "devDependencies": { + "@babel/cli": "^7.6.0", "@babel/core": "^7.17.5", "@babel/eslint-parser": "^7.5.0", "@babel/plugin-proposal-class-properties": "^7.16.7", + "@babel/plugin-proposal-decorators": "^7.17.8", + "@babel/plugin-proposal-export-default-from": "^7.16.7", "@babel/plugin-proposal-object-rest-spread": "^7.17.3", + "@babel/plugin-proposal-pipeline-operator": "^7.17.6", "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@babel/plugin-transform-runtime": "^7.17.0", + "@babel/plugin-transform-template-literals": "^7.16.7", "@babel/preset-env": "^7.16.11", "@babel/preset-react": "^7.16.7", "@babel/register": "^7.17.0", "@babel/runtime": "^7.17.8", - "@babel/plugin-transform-runtime": "^7.17.0", - "@babel/plugin-proposal-export-default-from": "^7.16.7", - "@babel/plugin-transform-template-literals": "^7.16.7", - "@babel/plugin-proposal-decorators": "^7.17.8", - "@babel/plugin-proposal-pipeline-operator": "^7.17.6", - "@babel/plugin-transform-async-to-generator": "^7.16.8", + "@fortawesome/fontawesome-free": ">=6.6.0", "babel-jest": "^27.5.1", "babel-loader": "^8.2.3", "babel-plugin-minify-dead-code-elimination": "^0.5.1", - "@babel/cli": "^7.6.0", + "css-loader": "^3.6.0", "d3": ">=5.9.0", "eslint": "^8.10.0", "eslint-plugin-cypress": "^2.12.1", "eslint-plugin-react": "^7.29.2", "fancy-log": "^1.3.3", + "file-loader": "^4.3.0", "gulp": "^4.0.0", "http-server": "^0.12.1", "jasmine": "^3.5.0", - "sass": "^1.39.0", "plugin-error": "^1.0.1", "prop-types": "^15.7.2", "react": ">=16.14.0", "react-dom": ">=16.14.0", "react-transition-group": "^4.4.1", + "sass": "^1.39.0", "source-map-support": "^0.5.19", "string-replace-loader": "^2.3.0", + "style-loader": "^1.3.0", "terser-webpack-plugin": "^4.2.3", + "url-loader": "^2.3.0", "webpack": "^4.46.0" }, "peerDependencies": { From 519b33c60f7dc0897e119bcf4831e3122dea7c3c Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 23 Oct 2024 00:01:49 +0300 Subject: [PATCH 02/34] git and npm ignores --- .gitignore | 1 + .npmignore | 1 + 2 files changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index d1b1f57..8e59901 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ dist es demo/demo-compiled.js +demo/webfonts/* node_modules .DS_Store diff --git a/.npmignore b/.npmignore index eb14221..f2f25ec 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,5 @@ demo/demo.js +demo/webfonts/* node_modules src/components src/index.js From 7898c84044c84a037ce5470d137f48c2894de259 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 23 Oct 2024 00:02:24 +0300 Subject: [PATCH 03/34] update webpack demo settings for fontawesome --- webpack.config.demo.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/webpack.config.demo.js b/webpack.config.demo.js index 26b0c8a..f460e63 100644 --- a/webpack.config.demo.js +++ b/webpack.config.demo.js @@ -64,6 +64,22 @@ module.exports = [{ loader: 'babel-loader' } ] + }, + { + test: /\.css$/, + use: ['style-loader', 'css-loader'] + }, + { + test: /\.(woff|woff2|eot|ttf|otf|svg)$/, + use: [ + { + loader: 'file-loader', + options: { + name: '[name].[ext]', + outputPath: 'webfonts/', + } + } + ] } ] }, From b045edfd1b56f944b3549b7be65c66fd3e1c4c76 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 23 Oct 2024 00:02:54 +0300 Subject: [PATCH 04/34] update demo --- demo/demo.js | 1 + 1 file changed, 1 insertion(+) diff --git a/demo/demo.js b/demo/demo.js index 5ff738e..4f773d0 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom'; import _ from 'underscore'; import url from 'url'; import { default as packageJSON } from './../package.json'; +import '@fortawesome/fontawesome-free/css/all.css'; // Loaded on index.html, defined as an external in webpack.config.demo.js import Graph, { GraphParser } from 'react-workflow-viz'; From 9fd21977f8599adc3905eb34ebc8036847fa7114 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 23 Oct 2024 00:03:16 +0300 Subject: [PATCH 05/34] add zoom in/out styles --- src/styles.scss | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/styles.scss b/src/styles.scss index 47ab005..a36ab99 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -624,4 +624,72 @@ $workflow-node-color-type-global-context: #ffb3b3 !default; @include workflow-edge-layer; } } +} + +.zoom-controls-container { + position: absolute; + top: 8px; + left: 8px; + > .zoom-buttons-row { + display: flex; + align-items: center; + > .zoom-btn, + > .zoom-value { + box-sizing: content-box; + text-align: center; + padding: 1px 4px; + height: 25px; + color: #555; + background-color: #fff; + } + > .zoom-btn { + border: 1px solid #999; + width: 24px; + + &.zoom-out { // Left btn + border-radius: 5px 0 0 5px; + border-right-color: #ddd !important; + } + &.zoom-in { // Right btn + border-radius: 0 5px 5px 0; + border-left-color : #ddd !important; + } + + &:disabled { + color: #999; + pointer-events: none; + } + &:hover:not(:disabled), + &:focus:not(:disabled), &:active:not(:disabled) { + color: #000; + border-color: #555; + outline: none; + } + &:focus:not(:disabled), &:active:not(:disabled) { + box-shadow: inset 0px 0px 2px 0px #333; + } + } + > .zoom-value { + border-top: 1px solid #999999; + border-bottom: 1px solid #999999; + width: 36px; + font-size: 0.875em; + line-height: 25px; + white-space: nowrap; + text-align: right; + } + } + > .zoom-slider { + width: 112px; + padding-top: 3px; + > input[type="range"] { + width: inherit; + // If we want to make it vertical later: + //&[orient="vertical"] { + // writing-mode: bt-lr; /* IE */ + // -webkit-appearance: slider-vertical; /* WebKit */ + // height: 120px; + //} + } + } } \ No newline at end of file From fe3a779a2aa9243e3f161e0b7b248c3fb444f6c1 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 23 Oct 2024 00:03:51 +0300 Subject: [PATCH 06/34] add essential scale controller and controls components --- src/components/ScaleController.js | 461 ++++++++++++++++++++++++++++++ 1 file changed, 461 insertions(+) create mode 100644 src/components/ScaleController.js diff --git a/src/components/ScaleController.js b/src/components/ScaleController.js new file mode 100644 index 0000000..b686422 --- /dev/null +++ b/src/components/ScaleController.js @@ -0,0 +1,461 @@ +'use strict'; + +import React from 'react'; + + +export class ScaleController extends React.PureComponent { + + static defaultProps = { + minScale: 0.01, + maxScale: 1.25, + initialScale: 1, + zoomToExtentsOnMount: true + }; + + constructor(props){ + super(props); + this.setScale = this.setScale.bind(this); + this.handleWheelMove = this.handleWheelMove.bind(this); + this.handleInnerContainerMounted = this.handleInnerContainerMounted.bind(this); + this.handleInnerContainerWillUnmount = this.handleInnerContainerWillUnmount.bind(this); + this.state = { + scale: null, + minScale: null + }; + this.innerElemReference = null; + } + + componentDidMount(){ + const { + containerWidth, + containerHeight, + minScale: propMinScale, + maxScale, + graphWidth, + graphHeight, + zoomToExtentsOnMount = true + } = this.props; + + if (typeof containerWidth !== "number" || typeof containerHeight !== "number") { + // Maybe will become set in componentDidUpdate later. + return false; + } + + if (isNaN(containerWidth) || isNaN(containerHeight)) { + throw new Error("Width or height is NaN."); + } + + const minScaleUnbounded = Math.min( + (containerWidth / graphWidth), + (containerHeight / graphHeight) + ); + + // Decrease by 5% for scrollbars, etc. + const nextMinScale = Math.floor( + Math.min(1, maxScale, Math.max(propMinScale, minScaleUnbounded)) + * 95) / 100; + const retObj = { minScale: nextMinScale }; + + // First time that we've gotten dimensions -- set scale to fit. + // Also, if nextMinScale > scale or we had scale === minScale before. + // TODO: Maybe do this onMount also + if (zoomToExtentsOnMount) { + retObj.scale = nextMinScale; + } + requestAnimationFrame(() => { + this.setState(retObj); + }); + } + + componentDidUpdate(pastProps, pastState){ + const { + enableMouseWheelZoom, + enablePinchZoom, + zoomToExtentsOnMount, + containerWidth, + containerHeight, + graphWidth, + graphHeight, + minScale: propMinScale, + maxScale + } = this.props; + const { scale, minScale: stateMinScale } = this.state; + const { + enableMouseWheelZoom: pastWheelEnabled, + enablePinchZoom: pastPinchEnabled, + containerWidth: pastWidth, + containerHeight: pastHeight, + graphWidth: pastGraphWidth, + graphHeight: pastGraphHeight + } = pastProps; + + // Remove or attach listeners if needed. + const listenNow = (enableMouseWheelZoom || enablePinchZoom); + const listenBefore = (pastWheelEnabled || pastPinchEnabled); + + if (this.innerElemReference){ + if (listenNow && !listenBefore){ + this.innerElemReference.addEventListener("wheel", this.handleWheelMove, { "passive": false, "capture": true }); + } else if (!listenNow && listenBefore){ + this.innerElemReference.removeEventListener("wheel", this.handleWheelMove); + } + } + + // Update minScale (& possibly scale itself) + // We read `pastState` here before updating set + // vs using functional updater because want to avoid + // React's state change queuing mechanisms / reading + // most accurate prev value not important. + if (containerWidth !== pastWidth || + containerHeight !== pastHeight || + graphWidth !== pastGraphWidth || + graphHeight !== pastGraphHeight + ){ + const minScaleUnbounded = Math.min( + (containerWidth / graphWidth), + (containerHeight / graphHeight) + ); + + // Decrease by 5% for scrollbars, etc. + const nextMinScale = Math.floor( + Math.min(1, maxScale, Math.max(propMinScale, minScaleUnbounded)) + * 95) / 100; + const retObj = { minScale: nextMinScale }; + + // First time that we've gotten dimensions -- set scale to fit. + // Also, if nextMinScale > scale or we had scale === minScale before. + // TODO: Maybe do this onMount also + if (nextMinScale > scale || stateMinScale === scale || (zoomToExtentsOnMount && (!pastHeight || !pastWidth))) { + retObj.scale = nextMinScale; + } + requestAnimationFrame(() => { + this.setState(retObj); + }); + } + } + + setScale(scaleToSet, cb){ + this.setState(function( + { minScale: stateMinScale }, + { minScale: propMinScale, maxScale } + ){ + const scale = Math.max( + Math.min( + maxScale, + scaleToSet + ), + stateMinScale || propMinScale + ); + return { scale }; + }, cb); + } + + /** + * If `enableMouseWheelZoom` or `enablePinchZoom` are enabled, will + * zoom in response to mouse wheel events or ctrl+mousewheel & touchpad + * pinch events, respectively. + */ + handleWheelMove(evt){ + const { deltaY, deltaX, ctrlKey } = evt; + const { enableMouseWheelZoom, enablePinchZoom } = this.props; + + if (!enableMouseWheelZoom && !enablePinchZoom) { + return false; + } + + // Chrome registers touchpad-based pinching as scrollwheel with ctrlKey. + if (enablePinchZoom && !enableMouseWheelZoom && !ctrlKey) { + return false; + } + + if (!enablePinchZoom && enableMouseWheelZoom && ctrlKey) { + return false; + } + + if (enableMouseWheelZoom && Math.abs(deltaX) > 0){ + // Not perfect -- + // Make sure is mousewheel and not bidirectional touchpad, + // for which we might still wanna allow left/right movement. + return false; + } + + evt.preventDefault(); + evt.stopPropagation(); + + // `ctrlKey=true` implies 2nd ctrl key being pressed -or- touchpad pinching + const deltaMultiplier = ctrlKey ? 0.01 : 0.0005; + + // React uses own state change queuing system, which guessing + // gets bypassed w. raf, so below line might work better, since + // `state.scale` unchanged.. (vs. functional updater) + this.setScale(this.state.scale - (deltaY * deltaMultiplier)); + + + // if (this.nextAnimationFrame !== null){ + // caf(this.nextAnimationFrame); + // } + + // this.nextAnimationFrame = raf(() => { + // this.setState(function( + // { scale: prevScale = 1, minScale: stateMinScale }, + // { minScale: propMinScale, maxScale } + // ){ + // const scaleUnbounded = prevScale - (deltaY * deltaMultiplier); + // const scale = Math.min( + // maxScale, + // Math.max(stateMinScale || propMinScale, scaleUnbounded) + // ); + // return { scale }; + // }, ()=>{ + // this.nextAnimationFrame = null; + // }); + // }); + } + + handleInnerContainerMounted(innerElem){ + const { onMount, enableMouseWheelZoom, enablePinchZoom } = this.props; + if (typeof onMount === "function"){ + onMount(...arguments); + } + this.innerElemReference = innerElem; + // We need to listen to `wheel` events directly (not thru React) + // as react doesn't handle these (it seems / afaik). + if (enableMouseWheelZoom || enablePinchZoom) { + // Chrome & some other browsers set passive:true by default. + innerElem.addEventListener("wheel", this.handleWheelMove, { "passive": false, "capture": true }); + } + } + + handleInnerContainerWillUnmount(innerElem){ + const { onWillUnmount } = this.props; + if (typeof onMount === "function"){ + onWillUnmount(...arguments); + } + if (this.innerElemReference === null) { + console.error("No inner elem, exiting"); + return; + } + if (this.innerElemReference !== innerElem) { + throw new Error("Inner elem is different, exiting"); + } + this.innerElemReference.removeEventListener("wheel", this.handleWheelMove); + this.innerElemReference = null; + } + + render(){ + const { children, initialScale = null, minScale: propMinScale, ...passProps } = this.props; + const { scale, minScale } = this.state; + const childProps = { + ...passProps, + scale: scale || initialScale || 1, + minScale: minScale || propMinScale, + setScale: this.setScale, + onMount: this.handleInnerContainerMounted, + onWillUnmount: this.handleInnerContainerWillUnmount + }; + return React.Children.map(children, (child) => React.cloneElement(child, childProps) ); + } + +} + +/** + * Component which provides UI for adjusting scale and + * calls `ScaleController`'s `setScale` function. + * + * Uses `requestAnimationFrame` (`raf`) for smooth and performant + * zooming transitions. + * + * To assert whether `raf` makes a meaningful difference, try to comment out + * the `raf`-related lines in `onSliderChange` method (except for `setScale(nextVal)`) + * and compare performance/smoothness :-D + * + * React _does_ seem to use requestAnimationFrame under the hood but maybe only + * for batched updates, as animation frames aren't always requested (Chrome dev + * tools > performance > profiling). + * + * We're getting performance gain from using `raf` in onSliderChange potentially + * because we're listening to `SyntheticEvent`s passed in from React element, which + * may be throttled or deferred until after state changes (vs native events). + */ +export class ScaleControls extends React.PureComponent { + + static defaultProps = { + scaleChangeInterval: 15, // milliseconds + scaleChangeUpFactor: 1.025, + scaleChangeDownFactor: 0.975 + }; + + constructor(props){ + super(props); + this.onZoomOutDown = this.onZoomOutDown.bind(this); + this.onZoomOutUp = this.onZoomOutUp.bind(this); + this.onZoomInDown = this.onZoomInDown.bind(this); + this.onZoomInUp = this.onZoomInUp.bind(this); + this.cancelAnimationFrame = this.cancelAnimationFrame.bind(this); + this.onSliderChange = this.onSliderChange.bind(this); + this.state = { + zoomOutPressed: false, + zoomInPressed: false + }; + this.nextAnimationFrame = null; + } + + cancelAnimationFrame(){ + if (this.nextAnimationFrame !== null) { + cancelAnimationFrame(this.nextAnimationFrame); + this.nextAnimationFrame = null; + } + } + + onZoomOutDown(evt){ + evt.preventDefault(); + evt.stopPropagation(); + const { setScale, scaleChangeInterval, scaleChangeDownFactor, scale: initScale } = this.props; + this.setState({ zoomOutPressed: true }, ()=>{ + const start = Date.now(); + //const diff = (scaleChangeDownFactor * initScale) - initScale; + + const performZoom = () => { + const { scale, minScale } = this.props; + if (scale <= minScale){ // Button becomes disabled so `onZoomOutUp` is not guaranteed to be called. + this.setState({ zoomOutPressed: false }); + this.nextAnimationFrame = null; + return; + } + setScale( + //initScale + (diff * Math.floor((Date.now() - start) / scaleChangeInterval)) + initScale * + (scaleChangeDownFactor ** Math.floor((Date.now() - start) / scaleChangeInterval)) + ); + this.nextAnimationFrame = requestAnimationFrame(performZoom); + }; + + this.nextAnimationFrame = requestAnimationFrame(performZoom); + }); + } + + onZoomOutUp(evt){ + evt.preventDefault(); + evt.stopPropagation(); + this.cancelAnimationFrame(); + this.setState({ zoomOutPressed: false }); + } + + onZoomInDown(evt){ + evt.preventDefault(); + evt.stopPropagation(); + const { setScale, scaleChangeInterval, scaleChangeUpFactor, scale: initScale } = this.props; + this.setState({ zoomInPressed: true }, ()=>{ + const start = Date.now(); + //const diff = (scaleChangeUpFactor * initScale) - initScale; + + const performZoom = () => { + const { scale, maxScale } = this.props; + if (scale >= maxScale){ // Button becomes disabled so `onZoomOutUp` is not guaranteed to be called. + this.setState({ zoomInPressed: false }); + this.nextAnimationFrame = null; + return; + } + setScale( + //initScale + (diff * Math.floor((Date.now() - start) / scaleChangeInterval)) + initScale * + (scaleChangeUpFactor ** Math.floor((Date.now() - start) / scaleChangeInterval)) + ); + this.nextAnimationFrame = requestAnimationFrame(performZoom); + }; + + this.nextAnimationFrame = requestAnimationFrame(performZoom); + }); + } + + onZoomInUp(evt){ + evt.preventDefault(); + evt.stopPropagation(); + this.cancelAnimationFrame(); + this.setState({ zoomInPressed: false }); + } + + onSliderChange(evt){ + evt.preventDefault(); + evt.stopPropagation(); + const { setScale } = this.props; + const nextVal = parseFloat(evt.target.value); + this.cancelAnimationFrame(); + this.nextAnimationFrame = requestAnimationFrame(() => { + setScale(nextVal); + this.nextAnimationFrame = null; + }); + } + + render(){ + const { + scale = null, + setScale = null, + minScale = 0.1, + maxScale = 1 + } = this.props; + + if (typeof setScale !== "function" || typeof scale !== "number" || isNaN(scale)) { + return null; + } + + return ( +
+
+ +
+ { Math.round(scale * 100) } + +
+ +
+
+ +
+
+ ); + } +} + +export function scaledStyle(graphHeight, graphWidth, scale){ + return { + width: (graphWidth * scale), + height: (graphHeight * scale), + transform : "scale3d(" + scale + "," + scale + ",1)" + }; +} + +/** + * Helper function for window.requestAnimationFrame. Falls back to browser-prefixed versions if default not available, or falls back to setTimeout with 0ms delay if no requestAnimationFrame available at all. + * + * @param {function} cb - Callback method. + * @returns {undefined|string} Undefined or timeout ID if falling back to setTimeout. + */ +export function requestAnimationFrame(cb){ + if (/*!isServerSide() && */typeof window !== 'undefined'){ + if (typeof window.requestAnimationFrame !== 'undefined') return window.requestAnimationFrame(cb); + if (typeof window.webkitRequestAnimationFrame !== 'undefined') return window.webkitRequestAnimationFrame(cb); + if (typeof window.mozRequestAnimationFrame !== 'undefined') return window.mozRequestAnimationFrame(cb); + } + return setTimeout(cb, 0); // Mock it for old browsers and server-side. +} + +export function cancelAnimationFrame(identifier){ + if (/*!isServerSide() && */typeof window !== 'undefined'){ + if (typeof window.cancelAnimationFrame !== 'undefined') return window.cancelAnimationFrame(identifier); + if (typeof window.webkitCancelAnimationFrame !== 'undefined') return window.webkitCancelAnimationFrame(identifier); + if (typeof window.mozCancelAnimationFrame !== 'undefined') return window.mozCancelAnimationFrame(identifier); + } + return clearTimeout(identifier); // Mock it for old browsers and server-side. +} From 372131fe6911fd06527d18b94855922246546498 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 23 Oct 2024 00:04:12 +0300 Subject: [PATCH 07/34] integrate scales controls --- src/components/Graph.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/Graph.js b/src/components/Graph.js index 6d9c4d9..5cc68d1 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -12,6 +12,7 @@ import NodesLayer from './NodesLayer'; import EdgesLayer from './EdgesLayer'; import { DefaultDetailPane } from './DefaultDetailPane'; import { DefaultNodeElement } from './Node'; +import { ScaleController, ScaleControls } from './ScaleController'; import { parseAnalysisSteps, parseBasicIOAnalysisSteps } from './parsing-functions'; @@ -309,8 +310,11 @@ export default class Graph extends React.Component { - - + + + + + From 85a1cc3b461e1a119359acd5b89ecbaeaab1cfe1 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Thu, 24 Oct 2024 10:20:24 +0300 Subject: [PATCH 08/34] move animation frame funcs. to utilities --- src/utilities.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/utilities.js diff --git a/src/utilities.js b/src/utilities.js new file mode 100644 index 0000000..e26286f --- /dev/null +++ b/src/utilities.js @@ -0,0 +1,28 @@ +'use strict'; + +import React from 'react'; + +/** + * Helper function for window.requestAnimationFrame. Falls back to browser-prefixed versions if default not available, or falls back to setTimeout with 0ms delay if no requestAnimationFrame available at all. + * + * @param {function} cb - Callback method. + * @returns {undefined|string} Undefined or timeout ID if falling back to setTimeout. + */ +export function requestAnimationFrame(cb){ + if (/*!isServerSide() && */typeof window !== 'undefined'){ + if (typeof window.requestAnimationFrame !== 'undefined') return window.requestAnimationFrame(cb); + if (typeof window.webkitRequestAnimationFrame !== 'undefined') return window.webkitRequestAnimationFrame(cb); + if (typeof window.mozRequestAnimationFrame !== 'undefined') return window.mozRequestAnimationFrame(cb); + } + return setTimeout(cb, 0); // Mock it for old browsers and server-side. +} + +export function cancelAnimationFrame(identifier){ + if (/*!isServerSide() && */typeof window !== 'undefined'){ + if (typeof window.cancelAnimationFrame !== 'undefined') return window.cancelAnimationFrame(identifier); + if (typeof window.webkitCancelAnimationFrame !== 'undefined') return window.webkitCancelAnimationFrame(identifier); + if (typeof window.mozCancelAnimationFrame !== 'undefined') return window.mozCancelAnimationFrame(identifier); + } + return clearTimeout(identifier); // Mock it for old browsers and server-side. +} + From 36e9aeed3a4d9cf9b670802d37c0fd57e5d27c3e Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Thu, 24 Oct 2024 12:01:08 +0300 Subject: [PATCH 09/34] move setScale to Graph --- src/components/Graph.js | 91 +++++++++-- src/components/ScaleController.js | 257 +++++++++++------------------- 2 files changed, 176 insertions(+), 172 deletions(-) diff --git a/src/components/Graph.js b/src/components/Graph.js index 5cc68d1..bc77225 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -13,6 +13,7 @@ import EdgesLayer from './EdgesLayer'; import { DefaultDetailPane } from './DefaultDetailPane'; import { DefaultNodeElement } from './Node'; import { ScaleController, ScaleControls } from './ScaleController'; +import { requestAnimationFrame, cancelAnimationFrame } from '../utilities' import { parseAnalysisSteps, parseBasicIOAnalysisSteps } from './parsing-functions'; @@ -101,7 +102,11 @@ export default class Graph extends React.Component { return false; }, 'nodeClassName' : function(node){ return ''; }, - 'nodeEdgeLedgeWidths' : [3,5] + 'nodeEdgeLedgeWidths' : [3,5], + //scale + 'scale': 1, + 'minScale': 0.75, + 'maxScale': 1.25 }; static getHeightFromNodes(nodes, nodesPreSortFxn, rowSpacing){ @@ -149,12 +154,13 @@ export default class Graph extends React.Component { rowSpacing = 75, columnWidth = 150, columnSpacing = 56, - isNodeCurrentContext = false + isNodeCurrentContext = false, + scale = 1 ){ /** Vertically centers a single node within a column */ function centerNode(n){ - n.y = (contentHeight / 2) + innerMargin.top; + n.y = ((contentHeight / 2) + innerMargin.top) * scale; n.nodesInColumn = 1; n.indexInColumn = 0; } @@ -182,14 +188,14 @@ export default class Graph extends React.Component { else { var padding = Math.max(0, contentHeight - ((countInCol - 1) * rowSpacing)) / 2; _.forEach(nodesInColumn, function(nodeInCol, idx){ - nodeInCol.y = ((idx + 0) * rowSpacing) + (innerMargin.top) + padding; + nodeInCol.y = (((idx + 0) * rowSpacing) + innerMargin.top + padding) * scale; nodeInCol.nodesInColumn = countInCol; }); } } else if (rowSpacingType === 'stacked') { _.forEach(nodesInColumn, function(nodeInCol, idx){ if (!nodeInCol) return; - nodeInCol.y = (rowSpacing * idx) + innerMargin.top; //num + (this.props.innerMargin.top + verticalMargin); + nodeInCol.y = ((rowSpacing * idx) + innerMargin.top) * scale; nodeInCol.nodesInColumn = countInCol; }); } else if (rowSpacingType === 'wide') { @@ -200,7 +206,7 @@ export default class Graph extends React.Component { function(yCoordinate, idx){ var nodeInCol = nodesInColumn[idx]; if (!nodeInCol) return; - nodeInCol.y = yCoordinate + innerMargin.top; + nodeInCol.y = (yCoordinate + innerMargin.top) * scale; nodeInCol.nodesInColumn = countInCol; } ); @@ -224,7 +230,7 @@ export default class Graph extends React.Component { // Set correct X coordinate on each node depending on column and spacing prop. _.forEach(nodesWithCoords, (node, i) => { - node.x = (node.column * (columnWidth + columnSpacing)) + leftOffset; + node.x = ((node.column * (columnWidth + columnSpacing)) + leftOffset) * scale; }); // Finally, add boolean `isCurrentContext` flag to each node object if needed. @@ -241,8 +247,11 @@ export default class Graph extends React.Component { super(props); this.height = this.height.bind(this); this.nodesWithCoordinates = this.nodesWithCoordinates.bind(this); + this.setScale = this.setScale.bind(this); this.state = { - 'mounted' : false + mounted: false, + scale: null, + minScale: null }; this.memoized = { getHeightFromNodes: memoize(Graph.getHeightFromNodes), @@ -253,6 +262,62 @@ export default class Graph extends React.Component { componentDidMount(){ this.setState({ 'mounted' : true }); + + const { + containerWidth, + containerHeight, + minScale: propMinScale, + maxScale, + graphWidth, + graphHeight, + zoomToExtentsOnMount = true + } = this.props; + + // if (typeof containerWidth !== "number" || typeof containerHeight !== "number") { + // // Maybe will become set in componentDidUpdate later. + // return false; + // } + + // if (isNaN(containerWidth) || isNaN(containerHeight)) { + // throw new Error("Width or height is NaN."); + // } + + // const minScaleUnbounded = Math.min( + // (containerWidth / graphWidth), + // (containerHeight / graphHeight) + // ); + + // // Decrease by 5% for scrollbars, etc. + // const nextMinScale = Math.floor( + // Math.min(1, maxScale, Math.max(propMinScale, minScaleUnbounded)) + // * 95) / 100; + // const retObj = { minScale: nextMinScale, mounted: true }; + + // // First time that we've gotten dimensions -- set scale to fit. + // // Also, if nextMinScale > scale or we had scale === minScale before. + // // TODO: Maybe do this onMount also + // if (zoomToExtentsOnMount) { + // retObj.scale = nextMinScale; + // } + // requestAnimationFrame(() => { + // this.setState(retObj); + // }); + } + + setScale(scaleToSet, cb){ + this.setState(function( + { minScale: stateMinScale }, + { minScale: propMinScale, maxScale } + ){ + const scale = Math.max( + Math.min( + maxScale, + scaleToSet + ), + stateMinScale || propMinScale + ); + return { scale }; + }, cb); } height() { @@ -267,14 +332,18 @@ export default class Graph extends React.Component { nodesWithCoordinates(viewportWidth, contentWidth, contentHeight){ const { nodes, innerMargin, rowSpacingType, rowSpacing, columnWidth, columnSpacing, isNodeCurrentContext } = this.props; + const { scale } = this.state; return this.memoized.getNodesWithCoordinates( nodes, viewportWidth, contentWidth, contentHeight, innerMargin, - rowSpacingType, rowSpacing, columnWidth, columnSpacing, isNodeCurrentContext + rowSpacingType, rowSpacing, columnWidth, columnSpacing, isNodeCurrentContext, scale || 1 ); } render(){ - const { width, innerMargin, edges, minimumHeight } = this.props; + const { + width, innerMargin, edges, minimumHeight, + scale: propScale = 1, maxScale: propMaxScale = 1.1, minScale: propMinScale = 0.9 + } = this.props; const { mounted } = this.state; const innerHeight = this.height(); const contentWidth = this.scrollableWidth(); @@ -310,7 +379,7 @@ export default class Graph extends React.Component { - + diff --git a/src/components/ScaleController.js b/src/components/ScaleController.js index b686422..1ac451a 100644 --- a/src/components/ScaleController.js +++ b/src/components/ScaleController.js @@ -1,6 +1,7 @@ 'use strict'; import React from 'react'; +import { requestAnimationFrame, cancelAnimationFrame } from '../utilities' export class ScaleController extends React.PureComponent { @@ -14,57 +15,52 @@ export class ScaleController extends React.PureComponent { constructor(props){ super(props); - this.setScale = this.setScale.bind(this); this.handleWheelMove = this.handleWheelMove.bind(this); this.handleInnerContainerMounted = this.handleInnerContainerMounted.bind(this); this.handleInnerContainerWillUnmount = this.handleInnerContainerWillUnmount.bind(this); - this.state = { - scale: null, - minScale: null - }; this.innerElemReference = null; } componentDidMount(){ - const { - containerWidth, - containerHeight, - minScale: propMinScale, - maxScale, - graphWidth, - graphHeight, - zoomToExtentsOnMount = true - } = this.props; - - if (typeof containerWidth !== "number" || typeof containerHeight !== "number") { - // Maybe will become set in componentDidUpdate later. - return false; - } - - if (isNaN(containerWidth) || isNaN(containerHeight)) { - throw new Error("Width or height is NaN."); - } + // const { + // containerWidth, + // containerHeight, + // minScale: propMinScale, + // maxScale, + // graphWidth, + // graphHeight, + // zoomToExtentsOnMount = true + // } = this.props; + + // if (typeof containerWidth !== "number" || typeof containerHeight !== "number") { + // // Maybe will become set in componentDidUpdate later. + // return false; + // } - const minScaleUnbounded = Math.min( - (containerWidth / graphWidth), - (containerHeight / graphHeight) - ); + // if (isNaN(containerWidth) || isNaN(containerHeight)) { + // throw new Error("Width or height is NaN."); + // } - // Decrease by 5% for scrollbars, etc. - const nextMinScale = Math.floor( - Math.min(1, maxScale, Math.max(propMinScale, minScaleUnbounded)) - * 95) / 100; - const retObj = { minScale: nextMinScale }; - - // First time that we've gotten dimensions -- set scale to fit. - // Also, if nextMinScale > scale or we had scale === minScale before. - // TODO: Maybe do this onMount also - if (zoomToExtentsOnMount) { - retObj.scale = nextMinScale; - } - requestAnimationFrame(() => { - this.setState(retObj); - }); + // const minScaleUnbounded = Math.min( + // (containerWidth / graphWidth), + // (containerHeight / graphHeight) + // ); + + // // Decrease by 5% for scrollbars, etc. + // const nextMinScale = Math.floor( + // Math.min(1, maxScale, Math.max(propMinScale, minScaleUnbounded)) + // * 95) / 100; + // const retObj = { minScale: nextMinScale }; + + // // First time that we've gotten dimensions -- set scale to fit. + // // Also, if nextMinScale > scale or we had scale === minScale before. + // // TODO: Maybe do this onMount also + // if (zoomToExtentsOnMount) { + // retObj.scale = nextMinScale; + // } + // requestAnimationFrame(() => { + // this.setState(retObj); + // }); } componentDidUpdate(pastProps, pastState){ @@ -79,76 +75,61 @@ export class ScaleController extends React.PureComponent { minScale: propMinScale, maxScale } = this.props; - const { scale, minScale: stateMinScale } = this.state; - const { - enableMouseWheelZoom: pastWheelEnabled, - enablePinchZoom: pastPinchEnabled, - containerWidth: pastWidth, - containerHeight: pastHeight, - graphWidth: pastGraphWidth, - graphHeight: pastGraphHeight - } = pastProps; - - // Remove or attach listeners if needed. - const listenNow = (enableMouseWheelZoom || enablePinchZoom); - const listenBefore = (pastWheelEnabled || pastPinchEnabled); - - if (this.innerElemReference){ - if (listenNow && !listenBefore){ - this.innerElemReference.addEventListener("wheel", this.handleWheelMove, { "passive": false, "capture": true }); - } else if (!listenNow && listenBefore){ - this.innerElemReference.removeEventListener("wheel", this.handleWheelMove); - } - } + // const { scale, minScale: stateMinScale } = this.state; + // const { + // enableMouseWheelZoom: pastWheelEnabled, + // enablePinchZoom: pastPinchEnabled, + // containerWidth: pastWidth, + // containerHeight: pastHeight, + // graphWidth: pastGraphWidth, + // graphHeight: pastGraphHeight + // } = pastProps; + + // // Remove or attach listeners if needed. + // const listenNow = (enableMouseWheelZoom || enablePinchZoom); + // const listenBefore = (pastWheelEnabled || pastPinchEnabled); + + // if (this.innerElemReference){ + // if (listenNow && !listenBefore){ + // this.innerElemReference.addEventListener("wheel", this.handleWheelMove, { "passive": false, "capture": true }); + // } else if (!listenNow && listenBefore){ + // this.innerElemReference.removeEventListener("wheel", this.handleWheelMove); + // } + // } - // Update minScale (& possibly scale itself) - // We read `pastState` here before updating set - // vs using functional updater because want to avoid - // React's state change queuing mechanisms / reading - // most accurate prev value not important. - if (containerWidth !== pastWidth || - containerHeight !== pastHeight || - graphWidth !== pastGraphWidth || - graphHeight !== pastGraphHeight - ){ - const minScaleUnbounded = Math.min( - (containerWidth / graphWidth), - (containerHeight / graphHeight) - ); - - // Decrease by 5% for scrollbars, etc. - const nextMinScale = Math.floor( - Math.min(1, maxScale, Math.max(propMinScale, minScaleUnbounded)) - * 95) / 100; - const retObj = { minScale: nextMinScale }; - - // First time that we've gotten dimensions -- set scale to fit. - // Also, if nextMinScale > scale or we had scale === minScale before. - // TODO: Maybe do this onMount also - if (nextMinScale > scale || stateMinScale === scale || (zoomToExtentsOnMount && (!pastHeight || !pastWidth))) { - retObj.scale = nextMinScale; - } - requestAnimationFrame(() => { - this.setState(retObj); - }); - } + // // Update minScale (& possibly scale itself) + // // We read `pastState` here before updating set + // // vs using functional updater because want to avoid + // // React's state change queuing mechanisms / reading + // // most accurate prev value not important. + // if (containerWidth !== pastWidth || + // containerHeight !== pastHeight || + // graphWidth !== pastGraphWidth || + // graphHeight !== pastGraphHeight + // ){ + // const minScaleUnbounded = Math.min( + // (containerWidth / graphWidth), + // (containerHeight / graphHeight) + // ); + + // // Decrease by 5% for scrollbars, etc. + // const nextMinScale = Math.floor( + // Math.min(1, maxScale, Math.max(propMinScale, minScaleUnbounded)) + // * 95) / 100; + // const retObj = { minScale: nextMinScale }; + + // // First time that we've gotten dimensions -- set scale to fit. + // // Also, if nextMinScale > scale or we had scale === minScale before. + // // TODO: Maybe do this onMount also + // if (nextMinScale > scale || stateMinScale === scale || (zoomToExtentsOnMount && (!pastHeight || !pastWidth))) { + // retObj.scale = nextMinScale; + // } + // requestAnimationFrame(() => { + // this.setState(retObj); + // }); + // } } - setScale(scaleToSet, cb){ - this.setState(function( - { minScale: stateMinScale }, - { minScale: propMinScale, maxScale } - ){ - const scale = Math.max( - Math.min( - maxScale, - scaleToSet - ), - stateMinScale || propMinScale - ); - return { scale }; - }, cb); - } /** * If `enableMouseWheelZoom` or `enablePinchZoom` are enabled, will @@ -157,7 +138,7 @@ export class ScaleController extends React.PureComponent { */ handleWheelMove(evt){ const { deltaY, deltaX, ctrlKey } = evt; - const { enableMouseWheelZoom, enablePinchZoom } = this.props; + const { enableMouseWheelZoom, enablePinchZoom, scale, setScale } = this.props; if (!enableMouseWheelZoom && !enablePinchZoom) { return false; @@ -188,28 +169,7 @@ export class ScaleController extends React.PureComponent { // React uses own state change queuing system, which guessing // gets bypassed w. raf, so below line might work better, since // `state.scale` unchanged.. (vs. functional updater) - this.setScale(this.state.scale - (deltaY * deltaMultiplier)); - - - // if (this.nextAnimationFrame !== null){ - // caf(this.nextAnimationFrame); - // } - - // this.nextAnimationFrame = raf(() => { - // this.setState(function( - // { scale: prevScale = 1, minScale: stateMinScale }, - // { minScale: propMinScale, maxScale } - // ){ - // const scaleUnbounded = prevScale - (deltaY * deltaMultiplier); - // const scale = Math.min( - // maxScale, - // Math.max(stateMinScale || propMinScale, scaleUnbounded) - // ); - // return { scale }; - // }, ()=>{ - // this.nextAnimationFrame = null; - // }); - // }); + setScale(scale - (deltaY * deltaMultiplier)); } handleInnerContainerMounted(innerElem){ @@ -243,13 +203,12 @@ export class ScaleController extends React.PureComponent { } render(){ - const { children, initialScale = null, minScale: propMinScale, ...passProps } = this.props; - const { scale, minScale } = this.state; + const { children, initialScale = null, scale, setScale, minScale, ...passProps } = this.props; const childProps = { ...passProps, - scale: scale || initialScale || 1, - minScale: minScale || propMinScale, - setScale: this.setScale, + scale: scale || 1, + minScale: minScale, + setScale: setScale, onMount: this.handleInnerContainerMounted, onWillUnmount: this.handleInnerContainerWillUnmount }; @@ -435,27 +394,3 @@ export function scaledStyle(graphHeight, graphWidth, scale){ transform : "scale3d(" + scale + "," + scale + ",1)" }; } - -/** - * Helper function for window.requestAnimationFrame. Falls back to browser-prefixed versions if default not available, or falls back to setTimeout with 0ms delay if no requestAnimationFrame available at all. - * - * @param {function} cb - Callback method. - * @returns {undefined|string} Undefined or timeout ID if falling back to setTimeout. - */ -export function requestAnimationFrame(cb){ - if (/*!isServerSide() && */typeof window !== 'undefined'){ - if (typeof window.requestAnimationFrame !== 'undefined') return window.requestAnimationFrame(cb); - if (typeof window.webkitRequestAnimationFrame !== 'undefined') return window.webkitRequestAnimationFrame(cb); - if (typeof window.mozRequestAnimationFrame !== 'undefined') return window.mozRequestAnimationFrame(cb); - } - return setTimeout(cb, 0); // Mock it for old browsers and server-side. -} - -export function cancelAnimationFrame(identifier){ - if (/*!isServerSide() && */typeof window !== 'undefined'){ - if (typeof window.cancelAnimationFrame !== 'undefined') return window.cancelAnimationFrame(identifier); - if (typeof window.webkitCancelAnimationFrame !== 'undefined') return window.webkitCancelAnimationFrame(identifier); - if (typeof window.mozCancelAnimationFrame !== 'undefined') return window.mozCancelAnimationFrame(identifier); - } - return clearTimeout(identifier); // Mock it for old browsers and server-side. -} From 381f789ea753681bfe67da009ea2076601d53d20 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 23 Oct 2024 18:04:42 +0300 Subject: [PATCH 10/34] scale nodes --- src/components/Node.js | 43 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/components/Node.js b/src/components/Node.js index 61ca5eb..7f51db4 100644 --- a/src/components/Node.js +++ b/src/components/Node.js @@ -70,9 +70,9 @@ export class DefaultNodeElement extends React.PureComponent { } render(){ - const { node, title, columnWidth } = this.props; + const { node, title, columnWidth, scale = 1 } = this.props; const style = node.nodeType === 'input' || node.nodeType === 'output' ? - { width : (columnWidth || 100) } + { width : (columnWidth || 100) * scale } : null; return (
From 6372332ac55783991d174370858197fc3ffff641 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Thu, 24 Oct 2024 15:47:40 +0300 Subject: [PATCH 11/34] remove unnecessary scales --- src/components/Node.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Node.js b/src/components/Node.js index 7f51db4..2f6ad17 100644 --- a/src/components/Node.js +++ b/src/components/Node.js @@ -240,8 +240,8 @@ export default class Node extends React.Component { data-node-selected={selected} data-node-in-selection-path={inSelectionPath} data-node-related={related} data-node-type-detail={node.ioType && node.ioType.toLowerCase()} data-node-column={node.column} style={{ - top: node.y * scale, - left: node.x * scale, + top: node.y, + left: node.x, width: (columnWidth || 100) * scale, zIndex: 2 + (node.indexInColumn || 0), transform: `scale(${scale})` From 55c7626c99645364fa98649d2a276ba0d320da2e Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Thu, 24 Oct 2024 15:59:05 +0300 Subject: [PATCH 12/34] add scale to `traceEdges` --- src/components/EdgesLayer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/EdgesLayer.js b/src/components/EdgesLayer.js index 8de8462..555acb6 100644 --- a/src/components/EdgesLayer.js +++ b/src/components/EdgesLayer.js @@ -412,12 +412,12 @@ export default class EdgesLayer extends React.PureComponent { const { outerHeight, innerWidth, innerMargin, width, edges: origEdges, nodes, selectedNode, isNodeDisabled, contentWidth, - columnWidth, columnSpacing, rowSpacing, innerHeight + columnWidth, columnSpacing, rowSpacing, innerHeight, scale = 1 } = this.props; const { edges, horizontalSegments - } = traceEdges(origEdges, nodes, columnWidth, columnSpacing, rowSpacing, contentWidth, innerHeight, innerMargin); + } = traceEdges(origEdges, nodes, columnWidth * scale, columnSpacing * scale, rowSpacing * scale, contentWidth, innerHeight, innerMargin); const edgeCount = edges.length; const divWidth = Math.max(width, contentWidth); From 320bc5ac2629b0d16928782a212e9d4c0dc373f2 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Thu, 24 Oct 2024 15:59:26 +0300 Subject: [PATCH 13/34] initialize state --- src/components/Graph.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/Graph.js b/src/components/Graph.js index bc77225..4f469ca 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -250,8 +250,8 @@ export default class Graph extends React.Component { this.setScale = this.setScale.bind(this); this.state = { mounted: false, - scale: null, - minScale: null + scale: props.scale, + minScale: props.minScale }; this.memoized = { getHeightFromNodes: memoize(Graph.getHeightFromNodes), @@ -322,12 +322,14 @@ export default class Graph extends React.Component { height() { const { nodes, nodesPreSortFxn, rowSpacing } = this.props; - return this.memoized.getHeightFromNodes(nodes, nodesPreSortFxn, rowSpacing); + const { scale } = this.state; + return this.memoized.getHeightFromNodes(nodes, nodesPreSortFxn, rowSpacing * scale); } scrollableWidth(){ const { nodes, columnWidth, columnSpacing, innerMargin } = this.props; - return this.memoized.getScrollableWidthFromNodes(nodes, columnWidth, columnSpacing, innerMargin); + const { scale } = this.state; + return this.memoized.getScrollableWidthFromNodes(nodes, columnWidth * scale, columnSpacing * scale, innerMargin); } nodesWithCoordinates(viewportWidth, contentWidth, contentHeight){ From 43bc1b482d95eacefa8e920765205108875c777b Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Mon, 28 Oct 2024 10:53:32 +0300 Subject: [PATCH 14/34] scale traceEdges --- src/components/EdgesLayer.js | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/components/EdgesLayer.js b/src/components/EdgesLayer.js index 555acb6..437bf3c 100644 --- a/src/components/EdgesLayer.js +++ b/src/components/EdgesLayer.js @@ -410,14 +410,23 @@ export default class EdgesLayer extends React.PureComponent { */ render(){ const { - outerHeight, innerWidth, innerMargin, width, edges: origEdges, nodes, + outerHeight, innerWidth, innerMargin: propInnerMargin, width, edges: origEdges, nodes, selectedNode, isNodeDisabled, contentWidth, - columnWidth, columnSpacing, rowSpacing, innerHeight, scale = 1 + columnWidth: propColumnWidth, columnSpacing: propColumnSpacing, rowSpacing: propRowSpacing, innerHeight, scale = 1 } = this.props; + const columnWidth = propColumnWidth * scale; + const columnSpacing = propColumnSpacing * scale; + const rowSpacing = propRowSpacing * scale; + const innerMargin = { + top: propInnerMargin.top * scale, + right: propInnerMargin.right * scale, + bottom: propInnerMargin.bottom * scale, + left: propInnerMargin.left * scale, + }; const { edges, horizontalSegments - } = traceEdges(origEdges, nodes, columnWidth * scale, columnSpacing * scale, rowSpacing * scale, contentWidth, innerHeight, innerMargin); + } = traceEdges(origEdges, nodes, columnWidth, columnSpacing, rowSpacing, contentWidth, innerHeight, innerMargin); const edgeCount = edges.length; const divWidth = Math.max(width, contentWidth); @@ -444,8 +453,8 @@ export default class EdgesLayer extends React.PureComponent { onExit={() => EdgesLayer.edgeOnExit(this.nodeRefs[key])} nodeRef={this.nodeRefs[key]}> Date: Mon, 28 Oct 2024 10:53:52 +0300 Subject: [PATCH 15/34] scale path offsets --- src/components/Edge.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Edge.js b/src/components/Edge.js index 5639cd8..c4e72b4 100644 --- a/src/components/Edge.js +++ b/src/components/Edge.js @@ -416,14 +416,14 @@ export default class Edge extends React.Component { } getPathOffsets(startOffset = 5, endOffset = -5, props = this.props){ - const { edge, pathArrows } = props; + const { edge, pathArrows, scale } = props; const { disabled, selected, related, distantlySelected } = this.getComputedProperties(props); if (pathArrows) endOffset -= 10; if (selected || related) endOffset -= 5; if (distantlySelected) endOffset -= 2; if (edge.source.isCurrentContext) startOffset += 5; if (edge.target.isCurrentContext) endOffset -= 5; - return { startOffset, endOffset }; + return { startOffset: startOffset * scale, endOffset: endOffset * scale }; } generatePathDimension(startPtOverride = null, endPtOverride = null, edgeVerticesOverride = null){ From 9ece73c0493a936d0ee9a19c6d2a199d7dd4ea00 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Mon, 28 Oct 2024 12:46:09 +0300 Subject: [PATCH 16/34] add roundScaled util function --- src/utilities.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utilities.js b/src/utilities.js index e26286f..b30d0f0 100644 --- a/src/utilities.js +++ b/src/utilities.js @@ -26,3 +26,6 @@ export function cancelAnimationFrame(identifier){ return clearTimeout(identifier); // Mock it for old browsers and server-side. } +export function roundScaled(value, scale, round = 2) { + return parseFloat((value * scale).toFixed(round)); +} \ No newline at end of file From 220b23f88783a20fe041da7a996aeac565a61564 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Mon, 28 Oct 2024 12:48:08 +0300 Subject: [PATCH 17/34] round scaled values --- src/components/EdgesLayer.js | 15 ++++++++------- src/components/Graph.js | 16 ++++++++-------- src/components/Node.js | 5 +++-- src/components/ScaleController.js | 6 +++--- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/components/EdgesLayer.js b/src/components/EdgesLayer.js index 437bf3c..2f25513 100644 --- a/src/components/EdgesLayer.js +++ b/src/components/EdgesLayer.js @@ -8,6 +8,7 @@ import { TransitionGroup, Transition } from 'react-transition-group'; import { path as d3Path } from 'd3'; import Edge from './Edge'; +import { roundScaled } from '../utilities'; @@ -414,14 +415,14 @@ export default class EdgesLayer extends React.PureComponent { selectedNode, isNodeDisabled, contentWidth, columnWidth: propColumnWidth, columnSpacing: propColumnSpacing, rowSpacing: propRowSpacing, innerHeight, scale = 1 } = this.props; - const columnWidth = propColumnWidth * scale; - const columnSpacing = propColumnSpacing * scale; - const rowSpacing = propRowSpacing * scale; + const columnWidth = roundScaled(propColumnWidth, scale); + const columnSpacing = roundScaled(propColumnSpacing, scale); + const rowSpacing = roundScaled(propRowSpacing, scale); const innerMargin = { - top: propInnerMargin.top * scale, - right: propInnerMargin.right * scale, - bottom: propInnerMargin.bottom * scale, - left: propInnerMargin.left * scale, + top: roundScaled(propInnerMargin.top, scale), + right: roundScaled(propInnerMargin.right, scale), + bottom: roundScaled(propInnerMargin.bottom, scale), + left: roundScaled(propInnerMargin.left, scale), }; const { edges, diff --git a/src/components/Graph.js b/src/components/Graph.js index 4f469ca..2dffa0e 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -13,7 +13,7 @@ import EdgesLayer from './EdgesLayer'; import { DefaultDetailPane } from './DefaultDetailPane'; import { DefaultNodeElement } from './Node'; import { ScaleController, ScaleControls } from './ScaleController'; -import { requestAnimationFrame, cancelAnimationFrame } from '../utilities' +import { requestAnimationFrame, cancelAnimationFrame, roundScaled } from '../utilities' import { parseAnalysisSteps, parseBasicIOAnalysisSteps } from './parsing-functions'; @@ -160,7 +160,7 @@ export default class Graph extends React.Component { /** Vertically centers a single node within a column */ function centerNode(n){ - n.y = ((contentHeight / 2) + innerMargin.top) * scale; + n.y = roundScaled((contentHeight / 2) + innerMargin.top, scale); n.nodesInColumn = 1; n.indexInColumn = 0; } @@ -188,14 +188,14 @@ export default class Graph extends React.Component { else { var padding = Math.max(0, contentHeight - ((countInCol - 1) * rowSpacing)) / 2; _.forEach(nodesInColumn, function(nodeInCol, idx){ - nodeInCol.y = (((idx + 0) * rowSpacing) + innerMargin.top + padding) * scale; + nodeInCol.y = roundScaled(((idx + 0) * rowSpacing) + innerMargin.top + padding, scale); nodeInCol.nodesInColumn = countInCol; }); } } else if (rowSpacingType === 'stacked') { _.forEach(nodesInColumn, function(nodeInCol, idx){ if (!nodeInCol) return; - nodeInCol.y = ((rowSpacing * idx) + innerMargin.top) * scale; + nodeInCol.y = roundScaled((rowSpacing * idx) + innerMargin.top, scale); nodeInCol.nodesInColumn = countInCol; }); } else if (rowSpacingType === 'wide') { @@ -206,7 +206,7 @@ export default class Graph extends React.Component { function(yCoordinate, idx){ var nodeInCol = nodesInColumn[idx]; if (!nodeInCol) return; - nodeInCol.y = (yCoordinate + innerMargin.top) * scale; + nodeInCol.y = roundScaled(yCoordinate + innerMargin.top, scale); nodeInCol.nodesInColumn = countInCol; } ); @@ -230,7 +230,7 @@ export default class Graph extends React.Component { // Set correct X coordinate on each node depending on column and spacing prop. _.forEach(nodesWithCoords, (node, i) => { - node.x = ((node.column * (columnWidth + columnSpacing)) + leftOffset) * scale; + node.x = roundScaled((node.column * (columnWidth + columnSpacing)) + leftOffset, scale); }); // Finally, add boolean `isCurrentContext` flag to each node object if needed. @@ -323,13 +323,13 @@ export default class Graph extends React.Component { height() { const { nodes, nodesPreSortFxn, rowSpacing } = this.props; const { scale } = this.state; - return this.memoized.getHeightFromNodes(nodes, nodesPreSortFxn, rowSpacing * scale); + return this.memoized.getHeightFromNodes(nodes, nodesPreSortFxn, roundScaled(rowSpacing, scale)); } scrollableWidth(){ const { nodes, columnWidth, columnSpacing, innerMargin } = this.props; const { scale } = this.state; - return this.memoized.getScrollableWidthFromNodes(nodes, columnWidth * scale, columnSpacing * scale, innerMargin); + return this.memoized.getScrollableWidthFromNodes(nodes, roundScaled(columnWidth, scale), roundScaled(columnSpacing, scale), innerMargin); } nodesWithCoordinates(viewportWidth, contentWidth, contentHeight){ diff --git a/src/components/Node.js b/src/components/Node.js index 2f6ad17..ea07022 100644 --- a/src/components/Node.js +++ b/src/components/Node.js @@ -5,6 +5,7 @@ import PropTypes from 'prop-types'; import memoize from 'memoize-one'; import _ from 'underscore'; import { traceNodePathAndRun } from './parsing-functions'; +import { roundScaled } from '../utilities'; /** @todo separate methods out into functional components */ @@ -72,7 +73,7 @@ export class DefaultNodeElement extends React.PureComponent { render(){ const { node, title, columnWidth, scale = 1 } = this.props; const style = node.nodeType === 'input' || node.nodeType === 'output' ? - { width : (columnWidth || 100) * scale } + { width : roundScaled((columnWidth || 100), scale) } : null; return (
diff --git a/src/components/ScaleController.js b/src/components/ScaleController.js index 1ac451a..f6a5b4a 100644 --- a/src/components/ScaleController.js +++ b/src/components/ScaleController.js @@ -1,7 +1,7 @@ 'use strict'; import React from 'react'; -import { requestAnimationFrame, cancelAnimationFrame } from '../utilities' +import { requestAnimationFrame, cancelAnimationFrame, roundScaled } from '../utilities' export class ScaleController extends React.PureComponent { @@ -389,8 +389,8 @@ export class ScaleControls extends React.PureComponent { export function scaledStyle(graphHeight, graphWidth, scale){ return { - width: (graphWidth * scale), - height: (graphHeight * scale), + width: roundScaled(graphWidth, scale), + height: roundScaled(graphHeight, scale), transform : "scale3d(" + scale + "," + scale + ",1)" }; } From 3314c2046464adc4673cd446d79deae44ffe299d Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Mon, 28 Oct 2024 16:57:40 +0300 Subject: [PATCH 18/34] move most of the scaling calculations to Graph component --- src/components/Edge.js | 3 ++- src/components/EdgesLayer.js | 14 ++--------- src/components/Graph.js | 45 +++++++++++++++++++++++------------- src/components/Node.js | 6 ++--- 4 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/components/Edge.js b/src/components/Edge.js index c4e72b4..1d0c2de 100644 --- a/src/components/Edge.js +++ b/src/components/Edge.js @@ -423,7 +423,7 @@ export default class Edge extends React.Component { if (distantlySelected) endOffset -= 2; if (edge.source.isCurrentContext) startOffset += 5; if (edge.target.isCurrentContext) endOffset -= 5; - return { startOffset: startOffset * scale, endOffset: endOffset * scale }; + return { startOffset, endOffset }; } generatePathDimension(startPtOverride = null, endPtOverride = null, edgeVerticesOverride = null){ @@ -445,6 +445,7 @@ export default class Edge extends React.Component { if (customEdgeVertices || edgeVerticesOverride){ return this.memoized.d.drawBezierEdgeVertices(startPt, endPt, edgeVerticesOverride || customEdgeVertices, nodeEdgeLedgeWidths); + // return null; } if (edgeStyle === 'straight'){ diff --git a/src/components/EdgesLayer.js b/src/components/EdgesLayer.js index 2f25513..49c678c 100644 --- a/src/components/EdgesLayer.js +++ b/src/components/EdgesLayer.js @@ -8,7 +8,6 @@ import { TransitionGroup, Transition } from 'react-transition-group'; import { path as d3Path } from 'd3'; import Edge from './Edge'; -import { roundScaled } from '../utilities'; @@ -411,19 +410,10 @@ export default class EdgesLayer extends React.PureComponent { */ render(){ const { - outerHeight, innerWidth, innerMargin: propInnerMargin, width, edges: origEdges, nodes, + outerHeight, innerWidth, innerMargin, width, edges: origEdges, nodes, selectedNode, isNodeDisabled, contentWidth, - columnWidth: propColumnWidth, columnSpacing: propColumnSpacing, rowSpacing: propRowSpacing, innerHeight, scale = 1 + columnWidth, columnSpacing, rowSpacing, innerHeight } = this.props; - const columnWidth = roundScaled(propColumnWidth, scale); - const columnSpacing = roundScaled(propColumnSpacing, scale); - const rowSpacing = roundScaled(propRowSpacing, scale); - const innerMargin = { - top: roundScaled(propInnerMargin.top, scale), - right: roundScaled(propInnerMargin.right, scale), - bottom: roundScaled(propInnerMargin.bottom, scale), - left: roundScaled(propInnerMargin.left, scale), - }; const { edges, horizontalSegments diff --git a/src/components/Graph.js b/src/components/Graph.js index 2dffa0e..bd4841c 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -154,13 +154,11 @@ export default class Graph extends React.Component { rowSpacing = 75, columnWidth = 150, columnSpacing = 56, - isNodeCurrentContext = false, - scale = 1 + isNodeCurrentContext = false ){ - /** Vertically centers a single node within a column */ function centerNode(n){ - n.y = roundScaled((contentHeight / 2) + innerMargin.top, scale); + n.y = (contentHeight / 2) + innerMargin.top; n.nodesInColumn = 1; n.indexInColumn = 0; } @@ -188,14 +186,14 @@ export default class Graph extends React.Component { else { var padding = Math.max(0, contentHeight - ((countInCol - 1) * rowSpacing)) / 2; _.forEach(nodesInColumn, function(nodeInCol, idx){ - nodeInCol.y = roundScaled(((idx + 0) * rowSpacing) + innerMargin.top + padding, scale); + nodeInCol.y = ((idx + 0) * rowSpacing) + innerMargin.top + padding; nodeInCol.nodesInColumn = countInCol; }); } } else if (rowSpacingType === 'stacked') { _.forEach(nodesInColumn, function(nodeInCol, idx){ if (!nodeInCol) return; - nodeInCol.y = roundScaled((rowSpacing * idx) + innerMargin.top, scale); + nodeInCol.y = (rowSpacing * idx) + innerMargin.top; nodeInCol.nodesInColumn = countInCol; }); } else if (rowSpacingType === 'wide') { @@ -206,7 +204,7 @@ export default class Graph extends React.Component { function(yCoordinate, idx){ var nodeInCol = nodesInColumn[idx]; if (!nodeInCol) return; - nodeInCol.y = roundScaled(yCoordinate + innerMargin.top, scale); + nodeInCol.y = yCoordinate + innerMargin.top; nodeInCol.nodesInColumn = countInCol; } ); @@ -230,7 +228,7 @@ export default class Graph extends React.Component { // Set correct X coordinate on each node depending on column and spacing prop. _.forEach(nodesWithCoords, (node, i) => { - node.x = roundScaled((node.column * (columnWidth + columnSpacing)) + leftOffset, scale); + node.x = node.column * (columnWidth + columnSpacing) + leftOffset; }); // Finally, add boolean `isCurrentContext` flag to each node object if needed. @@ -337,21 +335,35 @@ export default class Graph extends React.Component { const { scale } = this.state; return this.memoized.getNodesWithCoordinates( nodes, viewportWidth, contentWidth, contentHeight, innerMargin, - rowSpacingType, rowSpacing, columnWidth, columnSpacing, isNodeCurrentContext, scale || 1 + rowSpacingType, + roundScaled(rowSpacing, scale), roundScaled(columnWidth, scale), roundScaled(columnSpacing, scale), + isNodeCurrentContext, scale || 1 ); } render(){ const { - width, innerMargin, edges, minimumHeight, + width, innerMargin: propInnerMargin, edges, minimumHeight, + columnSpacing: propColumnSpacing, rowSpacing: propRowSpacing, columnWidth: propColumnWidth, scale: propScale = 1, maxScale: propMaxScale = 1.1, minScale: propMinScale = 0.9 } = this.props; - const { mounted } = this.state; + const { mounted, scale: stateScale } = this.state; + const scale = stateScale || propScale; const innerHeight = this.height(); const contentWidth = this.scrollableWidth(); let innerWidth = width; - if (!mounted){ + const columnSpacing = roundScaled(propColumnSpacing, scale); + const rowSpacing = roundScaled(propRowSpacing, scale); + const columnWidth = roundScaled(propColumnWidth, scale); + const innerMargin = { + top: roundScaled(propInnerMargin.top, scale), + right: roundScaled(propInnerMargin.right, scale), + bottom: roundScaled(propInnerMargin.bottom, scale), + left: roundScaled(propInnerMargin.left, scale), + }; + + if (!mounted) { return (
 
@@ -378,12 +390,13 @@ export default class Graph extends React.Component { return (
- + - + - + diff --git a/src/components/Node.js b/src/components/Node.js index ea07022..c824b7e 100644 --- a/src/components/Node.js +++ b/src/components/Node.js @@ -73,7 +73,7 @@ export class DefaultNodeElement extends React.PureComponent { render(){ const { node, title, columnWidth, scale = 1 } = this.props; const style = node.nodeType === 'input' || node.nodeType === 'output' ? - { width : roundScaled((columnWidth || 100), scale) } + { width : columnWidth || roundScaled(100, scale) } : null; return (
From c9ad1101ab4d862428713ec6e855504b66910025 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Tue, 29 Oct 2024 10:13:29 +0300 Subject: [PATCH 19/34] remove obsolete scale prop --- src/components/Edge.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Edge.js b/src/components/Edge.js index 1d0c2de..5639cd8 100644 --- a/src/components/Edge.js +++ b/src/components/Edge.js @@ -416,7 +416,7 @@ export default class Edge extends React.Component { } getPathOffsets(startOffset = 5, endOffset = -5, props = this.props){ - const { edge, pathArrows, scale } = props; + const { edge, pathArrows } = props; const { disabled, selected, related, distantlySelected } = this.getComputedProperties(props); if (pathArrows) endOffset -= 10; if (selected || related) endOffset -= 5; @@ -445,7 +445,6 @@ export default class Edge extends React.Component { if (customEdgeVertices || edgeVerticesOverride){ return this.memoized.d.drawBezierEdgeVertices(startPt, endPt, edgeVerticesOverride || customEdgeVertices, nodeEdgeLedgeWidths); - // return null; } if (edgeStyle === 'straight'){ From e105219ebe40dc68d7a11f231420f5c3fd034c8a Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Tue, 29 Oct 2024 12:22:52 +0300 Subject: [PATCH 20/34] refactor converting prop values --- src/components/Graph.js | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/components/Graph.js b/src/components/Graph.js index bd4841c..a13871d 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -319,24 +319,37 @@ export default class Graph extends React.Component { } height() { - const { nodes, nodesPreSortFxn, rowSpacing } = this.props; + const { nodes, nodesPreSortFxn, rowSpacing: propRowSpacing } = this.props; const { scale } = this.state; - return this.memoized.getHeightFromNodes(nodes, nodesPreSortFxn, roundScaled(rowSpacing, scale)); + + const rowSpacing = roundScaled(propRowSpacing, scale); + return this.memoized.getHeightFromNodes(nodes, nodesPreSortFxn, rowSpacing); } scrollableWidth(){ - const { nodes, columnWidth, columnSpacing, innerMargin } = this.props; + const { nodes, columnWidth: propColumnWidth, columnSpacing: propColumnSpacing, innerMargin } = this.props; const { scale } = this.state; - return this.memoized.getScrollableWidthFromNodes(nodes, roundScaled(columnWidth, scale), roundScaled(columnSpacing, scale), innerMargin); + + const columnWidth = roundScaled(propColumnWidth, scale); + const columnSpacing = roundScaled(propColumnSpacing, scale); + return this.memoized.getScrollableWidthFromNodes(nodes, columnWidth, columnSpacing, innerMargin); } nodesWithCoordinates(viewportWidth, contentWidth, contentHeight){ - const { nodes, innerMargin, rowSpacingType, rowSpacing, columnWidth, columnSpacing, isNodeCurrentContext } = this.props; + const { + nodes, innerMargin, + rowSpacingType, rowSpacing: propRowSpacing, columnWidth: propColumnWidth, columnSpacing: propColumnSpacing, + isNodeCurrentContext + } = this.props; const { scale } = this.state; + + const rowSpacing = roundScaled(propRowSpacing, scale); + const columnWidth = roundScaled(propColumnWidth, scale); + const columnSpacing = roundScaled(propColumnSpacing, scale); + return this.memoized.getNodesWithCoordinates( nodes, viewportWidth, contentWidth, contentHeight, innerMargin, - rowSpacingType, - roundScaled(rowSpacing, scale), roundScaled(columnWidth, scale), roundScaled(columnSpacing, scale), + rowSpacingType, rowSpacing, columnWidth, columnSpacing, isNodeCurrentContext, scale || 1 ); } From d51faeed80fe2fb1fa62f14fa9fc11a036deb8b6 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Tue, 29 Oct 2024 12:42:28 +0300 Subject: [PATCH 21/34] more refactoring --- src/components/EdgesLayer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/EdgesLayer.js b/src/components/EdgesLayer.js index 49c678c..8de8462 100644 --- a/src/components/EdgesLayer.js +++ b/src/components/EdgesLayer.js @@ -444,8 +444,8 @@ export default class EdgesLayer extends React.PureComponent { onExit={() => EdgesLayer.edgeOnExit(this.nodeRefs[key])} nodeRef={this.nodeRefs[key]}> Date: Tue, 29 Oct 2024 13:04:39 +0300 Subject: [PATCH 22/34] add showZoomControls prop --- demo/demo.js | 2 +- src/components/Graph.js | 15 ++++++++++++--- src/components/ScaleController.js | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/demo/demo.js b/demo/demo.js index 4f773d0..58a80d0 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -181,7 +181,7 @@ class DemoApp extends Component { onChange={this.handleParsingOptChange} onChangeBasicIO={this.handleChangeBasicIO} /> - +
); diff --git a/src/components/Graph.js b/src/components/Graph.js index a13871d..46b001d 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -72,7 +72,12 @@ export default class Graph extends React.Component { 'capacity' : PropTypes.string })).isRequired, 'nodeTitle' : PropTypes.func, - 'rowSpacingType' : PropTypes.oneOf([ 'compact', 'wide', 'stacked' ]) + 'rowSpacingType' : PropTypes.oneOf([ 'compact', 'wide', 'stacked' ]), + //scale + 'showZoomControls': PropTypes.bool, + 'scale': PropTypes.number, + 'minScale': PropTypes.number, + 'maxScale': PropTypes.number }; static defaultProps = { @@ -104,6 +109,7 @@ export default class Graph extends React.Component { 'nodeClassName' : function(node){ return ''; }, 'nodeEdgeLedgeWidths' : [3,5], //scale + 'showZoomControls': true, 'scale': 1, 'minScale': 0.75, 'maxScale': 1.25 @@ -358,7 +364,8 @@ export default class Graph extends React.Component { const { width, innerMargin: propInnerMargin, edges, minimumHeight, columnSpacing: propColumnSpacing, rowSpacing: propRowSpacing, columnWidth: propColumnWidth, - scale: propScale = 1, maxScale: propMaxScale = 1.1, minScale: propMinScale = 0.9 + scale: propScale = 1, maxScale: propMaxScale = 1.1, minScale: propMinScale = 0.9, + showZoomControls } = this.props; const { mounted, scale: stateScale } = this.state; const scale = stateScale || propScale; @@ -407,7 +414,9 @@ export default class Graph extends React.Component { {..._.pick(this.props, 'pathArrows', 'href', 'onNodeClick', 'renderDetailPane')}> - + {showZoomControls && typeof this.setScale === "function" ? + + : null} diff --git a/src/components/ScaleController.js b/src/components/ScaleController.js index f6a5b4a..0f4bce8 100644 --- a/src/components/ScaleController.js +++ b/src/components/ScaleController.js @@ -212,7 +212,7 @@ export class ScaleController extends React.PureComponent { onMount: this.handleInnerContainerMounted, onWillUnmount: this.handleInnerContainerWillUnmount }; - return React.Children.map(children, (child) => React.cloneElement(child, childProps) ); + return React.Children.map(children, (child) => child && React.cloneElement(child, childProps) ); } } From 1c04a0260ccb38d8e15b45dc4edf61484e0c1629 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Tue, 29 Oct 2024 13:29:52 +0300 Subject: [PATCH 23/34] bump version(0.2.0-beta.0) --- package-lock.json | 85 ++--------------------------------------------- package.json | 2 +- 2 files changed, 3 insertions(+), 84 deletions(-) diff --git a/package-lock.json b/package-lock.json index f76b269..c147eeb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@hms-dbmi-bgm/react-workflow-viz", - "version": "0.1.11", + "version": "0.2.0-beta.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@hms-dbmi-bgm/react-workflow-viz", - "version": "0.1.11", + "version": "0.2.0-beta.0", "license": "MIT", "dependencies": { "memoize-one": "^5.1.1", @@ -49,7 +49,6 @@ "react-dom": ">=16.14.0", "react-transition-group": "^4.4.1", "sass": "^1.39.0", - "sass-loader": "^7.3.1", "source-map-support": "^0.5.19", "string-replace-loader": "^2.3.0", "style-loader": "^1.3.0", @@ -10499,51 +10498,6 @@ "node": ">=12.0.0" } }, - "node_modules/sass-loader": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", - "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "loader-utils": "^1.0.1", - "neo-async": "^2.5.0", - "pify": "^4.0.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">= 6.9.0" - }, - "peerDependencies": { - "webpack": "^3.0.0 || ^4.0.0" - } - }, - "node_modules/sass-loader/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/sass-loader/node_modules/loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/scheduler": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", @@ -21350,41 +21304,6 @@ "source-map-js": ">=0.6.2 <2.0.0" } }, - "sass-loader": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", - "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "loader-utils": "^1.0.1", - "neo-async": "^2.5.0", - "pify": "^4.0.1", - "semver": "^6.3.0" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "loader-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", - "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - } - } - }, "scheduler": { "version": "0.21.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.21.0.tgz", diff --git a/package.json b/package.json index f1efe2d..7a9caa6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hms-dbmi-bgm/react-workflow-viz", - "version": "0.1.11", + "version": "0.2.0-beta.0", "description": "React component for visualizing CWL-like workflows and provenance graphs.", "main": "./dist/react-workflow-viz.min.js", "unpkg": "./dist/react-workflow-viz.min.js", From f5bd12bc2060920498903870a4319ca8a796b03c Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Tue, 29 Oct 2024 16:22:45 +0300 Subject: [PATCH 24/34] remove defaults --- src/components/ScaleController.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/ScaleController.js b/src/components/ScaleController.js index 0f4bce8..5b04a13 100644 --- a/src/components/ScaleController.js +++ b/src/components/ScaleController.js @@ -347,12 +347,7 @@ export class ScaleControls extends React.PureComponent { } render(){ - const { - scale = null, - setScale = null, - minScale = 0.1, - maxScale = 1 - } = this.props; + const { scale, setScale, minScale, maxScale } = this.props; if (typeof setScale !== "function" || typeof scale !== "number" || isNaN(scale)) { return null; From 660c47d675bec6b37610b1c96fa7941f1a23f43d Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Tue, 29 Oct 2024 23:43:22 +0300 Subject: [PATCH 25/34] remove ScaleController container --- src/components/Graph.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/components/Graph.js b/src/components/Graph.js index 46b001d..8af447c 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -406,21 +406,22 @@ export default class Graph extends React.Component { graphHeight += (spacerCount * this.props.columnSpacing); } */ - + let scaleControls = null; + if (showZoomControls && typeof this.setScale === "function") { + const scaleProps = { scale, minScale: this.state.minScale || propMinScale, maxScale: propMaxScale, setScale: this.setScale }; + scaleControls = ; + } + return (
+ {scaleControls} - - {showZoomControls && typeof this.setScale === "function" ? - - : null} - - - + +
From a0558dfb5f6f94798ff6b12278836b5a8ad43ecf Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Tue, 29 Oct 2024 23:43:41 +0300 Subject: [PATCH 26/34] set ScaleController as depracated --- src/components/ScaleController.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/ScaleController.js b/src/components/ScaleController.js index 5b04a13..7782b07 100644 --- a/src/components/ScaleController.js +++ b/src/components/ScaleController.js @@ -4,6 +4,9 @@ import React from 'react'; import { requestAnimationFrame, cancelAnimationFrame, roundScaled } from '../utilities' +/** + * @deprecated Moved most of the functionality into Graph + */ export class ScaleController extends React.PureComponent { static defaultProps = { From eb27cec2542628f8f66d712b1bba8fd4c855d672 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Tue, 29 Oct 2024 23:43:56 +0300 Subject: [PATCH 27/34] set scale controls position --- src/styles.scss | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/styles.scss b/src/styles.scss index a36ab99..5795c63 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -536,7 +536,7 @@ $workflow-node-color-type-global-context: #ffb3b3 !default; } .scroll-container { - + display: flex; position: relative; box-sizing: content-box; transition: @@ -627,12 +627,15 @@ $workflow-node-color-type-global-context: #ffb3b3 !default; } .zoom-controls-container { + display: flex; position: absolute; + z-index: 999; top: 8px; - left: 8px; + left: 50%; + transform: translateX(-50%); > .zoom-buttons-row { display: flex; - align-items: center; + align-items: flex-start; > .zoom-btn, > .zoom-value { box-sizing: content-box; From c38033612b7f1ddfb759eecb2b81d983110005ad Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 30 Oct 2024 09:55:31 +0300 Subject: [PATCH 28/34] full-functional scale /w hard-coded columnWidth --- src/components/Graph.js | 2 +- src/components/Node.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Graph.js b/src/components/Graph.js index 8af447c..c2cdf4f 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -415,7 +415,7 @@ export default class Graph extends React.Component { return (
- {scaleControls} diff --git a/src/components/Node.js b/src/components/Node.js index c824b7e..aec1084 100644 --- a/src/components/Node.js +++ b/src/components/Node.js @@ -73,7 +73,7 @@ export class DefaultNodeElement extends React.PureComponent { render(){ const { node, title, columnWidth, scale = 1 } = this.props; const style = node.nodeType === 'input' || node.nodeType === 'output' ? - { width : columnWidth || roundScaled(100, scale) } + { width : 150 /*columnWidth || roundScaled(100, scale)*/ } : null; return (
From adcb4b59351434d17db3bfb5ec692dccaeb6a69b Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 30 Oct 2024 10:38:37 +0300 Subject: [PATCH 29/34] move scaling into NodesLayer and Nodes --- src/components/Graph.js | 7 ++++--- src/components/Node.js | 13 ++++++++----- src/components/NodesLayer.js | 15 ++++++++++++--- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/components/Graph.js b/src/components/Graph.js index c2cdf4f..68d741a 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -415,13 +415,14 @@ export default class Graph extends React.Component { return (
- {scaleControls} - - +
diff --git a/src/components/Node.js b/src/components/Node.js index aec1084..706d5b2 100644 --- a/src/components/Node.js +++ b/src/components/Node.js @@ -71,9 +71,9 @@ export class DefaultNodeElement extends React.PureComponent { } render(){ - const { node, title, columnWidth, scale = 1 } = this.props; + const { node, title, columnWidth } = this.props; const style = node.nodeType === 'input' || node.nodeType === 'output' ? - { width : 150 /*columnWidth || roundScaled(100, scale)*/ } + { width : columnWidth || 100 } : null; return (
1 && lastActiveContextNode === node)) @@ -242,8 +245,8 @@ export default class Node extends React.Component { data-node-related={related} data-node-type-detail={node.ioType && node.ioType.toLowerCase()} data-node-column={node.column} style={{ top: node.y, - left: node.x + ((scale - 1) * 75), - width: 150, //columnWidth || roundScaled(100, scale), + left: node.x + ((scale - 1) * columnWidth) / 2, + width: columnWidth || 100, zIndex: 2 + (node.indexInColumn || 0), transform: `scale(${scale})` }} ref={forwardedRef}> diff --git a/src/components/NodesLayer.js b/src/components/NodesLayer.js index e8cae9d..e8514d0 100644 --- a/src/components/NodesLayer.js +++ b/src/components/NodesLayer.js @@ -6,6 +6,7 @@ import memoize from 'memoize-one'; import { TransitionGroup, CSSTransition } from 'react-transition-group'; import Node from './Node'; +import { roundScaled } from '../utilities'; export default class NodesLayer extends React.PureComponent { @@ -73,9 +74,17 @@ export default class NodesLayer extends React.PureComponent { } render(){ - var { innerMargin, innerWidth, outerHeight, contentWidth } = this.props, - fullWidth = innerWidth + innerMargin.left + innerMargin.right, - layerStyle = { 'width' : Math.max(contentWidth, fullWidth), 'height' : outerHeight }; + const { innerMargin: propInnerMargin, innerWidth, outerHeight, contentWidth, scale } = this.props; + + const innerMargin = { + top: roundScaled(propInnerMargin.top, scale), + right: roundScaled(propInnerMargin.right, scale), + bottom: roundScaled(propInnerMargin.bottom, scale), + left: roundScaled(propInnerMargin.left, scale), + }; + + const fullWidth = innerWidth + innerMargin.left + innerMargin.right; + const layerStyle = { 'width': Math.max(contentWidth, fullWidth), 'height': outerHeight }; return (
From 3faa8d19364c9c984ec8ad6cf15564a3782f7b30 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 30 Oct 2024 12:19:08 +0300 Subject: [PATCH 30/34] move scaling into EdgesLayer and Edge --- src/components/Edge.js | 13 ++++++++++--- src/components/EdgesLayer.js | 19 +++++++++++++++---- src/components/Graph.js | 4 ++-- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/components/Edge.js b/src/components/Edge.js index 5639cd8..ecf1ed4 100644 --- a/src/components/Edge.js +++ b/src/components/Edge.js @@ -8,6 +8,7 @@ import * as d3 from 'd3'; import Node from './Node'; import { traceNodePathAndRun } from './parsing-functions'; +import { roundScaled } from '../utilities'; export const pathDimensionFunctions = { @@ -428,10 +429,16 @@ export default class Edge extends React.Component { generatePathDimension(startPtOverride = null, endPtOverride = null, edgeVerticesOverride = null){ const { - edgeStyle, startX, startY, endX, endY, columnWidth, - curveRadius, columnSpacing, rowSpacing, nodeEdgeLedgeWidths, - edge: { vertices: customEdgeVertices = null } + edgeStyle, startX, startY, endX, endY, curveRadius, + scale, columnWidth: propColumnWidth, columnSpacing: propColumnSpacing, rowSpacing: propRowSpacing, + nodeEdgeLedgeWidths, edge: { vertices: customEdgeVertices = null } } = this.props; + + // scaling + const columnWidth = roundScaled(propColumnWidth, scale); + const columnSpacing = roundScaled(propColumnSpacing, scale); + const rowSpacing = roundScaled(propRowSpacing, scale); + const { startOffset, endOffset } = this.getPathOffsets(); const startPt = { diff --git a/src/components/EdgesLayer.js b/src/components/EdgesLayer.js index 8de8462..881da2e 100644 --- a/src/components/EdgesLayer.js +++ b/src/components/EdgesLayer.js @@ -8,8 +8,7 @@ import { TransitionGroup, Transition } from 'react-transition-group'; import { path as d3Path } from 'd3'; import Edge from './Edge'; - - +import { roundScaled } from '../utilities'; @@ -410,10 +409,22 @@ export default class EdgesLayer extends React.PureComponent { */ render(){ const { - outerHeight, innerWidth, innerMargin, width, edges: origEdges, nodes, + outerHeight, innerWidth, innerHeight, width, edges: origEdges, nodes, selectedNode, isNodeDisabled, contentWidth, - columnWidth, columnSpacing, rowSpacing, innerHeight + scale, columnWidth: propColumnWidth, columnSpacing: propColumnSpacing, rowSpacing: propRowSpacing, innerMargin: propInnerMargin } = this.props; + + // scaling + const columnWidth = roundScaled(propColumnWidth, scale); + const columnSpacing = roundScaled(propColumnSpacing, scale); + const rowSpacing = roundScaled(propRowSpacing, scale); + const innerMargin = { + top: roundScaled(propInnerMargin.top, scale), + right: roundScaled(propInnerMargin.right, scale), + bottom: roundScaled(propInnerMargin.bottom, scale), + left: roundScaled(propInnerMargin.left, scale), + }; + const { edges, horizontalSegments diff --git a/src/components/Graph.js b/src/components/Graph.js index 68d741a..5ebf2df 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -419,9 +419,9 @@ export default class Graph extends React.Component { {..._.pick(this.props, 'pathArrows', 'href', 'onNodeClick', 'renderDetailPane')}> {scaleControls} - - From 85f1d6b1ddd470880bda7c7585bd03754edff968 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 30 Oct 2024 13:23:13 +0300 Subject: [PATCH 31/34] optimize `roundScaled` --- src/utilities.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/utilities.js b/src/utilities.js index b30d0f0..74ba619 100644 --- a/src/utilities.js +++ b/src/utilities.js @@ -26,6 +26,11 @@ export function cancelAnimationFrame(identifier){ return clearTimeout(identifier); // Mock it for old browsers and server-side. } -export function roundScaled(value, scale, round = 2) { - return parseFloat((value * scale).toFixed(round)); +export function roundScaled(value, scale, decimals = 2) { + //shortcut - useful for most cases + if (scale === 1) return value; + + const factor = Math.pow(10, decimals); + const result = value * scale; + return Math.round(result * factor) / factor; } \ No newline at end of file From 5e8d8b0a70889243bd0ef21a9045833d7fca8605 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 30 Oct 2024 14:42:07 +0300 Subject: [PATCH 32/34] set scale default value --- src/components/Edge.js | 2 +- src/components/EdgesLayer.js | 2 +- src/components/Node.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Edge.js b/src/components/Edge.js index ecf1ed4..8bb30a1 100644 --- a/src/components/Edge.js +++ b/src/components/Edge.js @@ -430,7 +430,7 @@ export default class Edge extends React.Component { generatePathDimension(startPtOverride = null, endPtOverride = null, edgeVerticesOverride = null){ const { edgeStyle, startX, startY, endX, endY, curveRadius, - scale, columnWidth: propColumnWidth, columnSpacing: propColumnSpacing, rowSpacing: propRowSpacing, + scale = 1, columnWidth: propColumnWidth, columnSpacing: propColumnSpacing, rowSpacing: propRowSpacing, nodeEdgeLedgeWidths, edge: { vertices: customEdgeVertices = null } } = this.props; diff --git a/src/components/EdgesLayer.js b/src/components/EdgesLayer.js index 881da2e..d943307 100644 --- a/src/components/EdgesLayer.js +++ b/src/components/EdgesLayer.js @@ -411,7 +411,7 @@ export default class EdgesLayer extends React.PureComponent { const { outerHeight, innerWidth, innerHeight, width, edges: origEdges, nodes, selectedNode, isNodeDisabled, contentWidth, - scale, columnWidth: propColumnWidth, columnSpacing: propColumnSpacing, rowSpacing: propRowSpacing, innerMargin: propInnerMargin + scale = 1, columnWidth: propColumnWidth, columnSpacing: propColumnSpacing, rowSpacing: propRowSpacing, innerMargin: propInnerMargin } = this.props; // scaling diff --git a/src/components/Node.js b/src/components/Node.js index 706d5b2..93401fc 100644 --- a/src/components/Node.js +++ b/src/components/Node.js @@ -188,7 +188,7 @@ export default class Node extends React.Component { componentDidMount(){ const { countInActiveContext, lastActiveContextNode, - node, scrollContainerWrapperElement, scale, columnWidth: propColumnWidth, columnSpacing: propColumnSpacing + node, scrollContainerWrapperElement, scale = 1, columnWidth: propColumnWidth, columnSpacing: propColumnSpacing } = this.props; const sw = scrollContainerWrapperElement; From 6c068cf23192f5be1afaf447fbbc987144ba5208 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 30 Oct 2024 17:27:58 +0300 Subject: [PATCH 33/34] scale controller style updates --- src/components/Graph.js | 6 +++--- src/styles.scss | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/Graph.js b/src/components/Graph.js index 5ebf2df..38f6b13 100644 --- a/src/components/Graph.js +++ b/src/components/Graph.js @@ -111,8 +111,8 @@ export default class Graph extends React.Component { //scale 'showZoomControls': true, 'scale': 1, - 'minScale': 0.75, - 'maxScale': 1.25 + 'minScale': 0.50, + 'maxScale': 1.50 }; static getHeightFromNodes(nodes, nodesPreSortFxn, rowSpacing){ @@ -415,9 +415,9 @@ export default class Graph extends React.Component { return (
+ {scaleControls} - {scaleControls} diff --git a/src/styles.scss b/src/styles.scss index 5795c63..cc3d179 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -685,6 +685,7 @@ $workflow-node-color-type-global-context: #ffb3b3 !default; > .zoom-slider { width: 112px; padding-top: 3px; + padding-left: 3px; > input[type="range"] { width: inherit; // If we want to make it vertical later: @@ -695,4 +696,7 @@ $workflow-node-color-type-global-context: #ffb3b3 !default; //} } } + + div.state-container { + margin-top: 40px; + } } \ No newline at end of file From 8526b2ada93dacdc00e556ad1b447e3c2ecf8217 Mon Sep 17 00:00:00 2001 From: utku-ozturk Date: Wed, 30 Oct 2024 17:30:23 +0300 Subject: [PATCH 34/34] bump version 0.2.0-beta.1 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c147eeb..72ca252 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@hms-dbmi-bgm/react-workflow-viz", - "version": "0.2.0-beta.0", + "version": "0.2.0-beta.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@hms-dbmi-bgm/react-workflow-viz", - "version": "0.2.0-beta.0", + "version": "0.2.0-beta.1", "license": "MIT", "dependencies": { "memoize-one": "^5.1.1", diff --git a/package.json b/package.json index 7a9caa6..ba24b20 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hms-dbmi-bgm/react-workflow-viz", - "version": "0.2.0-beta.0", + "version": "0.2.0-beta.1", "description": "React component for visualizing CWL-like workflows and provenance graphs.", "main": "./dist/react-workflow-viz.min.js", "unpkg": "./dist/react-workflow-viz.min.js",