From d688b81a80b6014fc73311af320c11a36266ff82 Mon Sep 17 00:00:00 2001 From: Moritz Kirstein Date: Mon, 14 Feb 2022 13:16:29 +0100 Subject: [PATCH] Feature: SVG Generator (#1032) * use smaller SVG for publishing test, siwtch to Buffer encoding/decoding * shorter NFT name & symbol * fix: fix small test svg * feat: first draft random wave svg generation * refactor: utilize join to remove unecessary ',' separator * feature: add gas cost estimation for nft artwork * refacotr: allow fillColor as array and adjust opacity * refactor: adjust random wave generation * refactor: change nft help text * refactor: trying tooltip for nft image info * feat: add custom algorithm to generate svg waves * fix: use text colors for nft form field svgs * refactor: code cleanup nft utils * refactor: improve readability * refactor: improve comment * refactor: (re)move comments, cleanup * refactor: remove console log * refactor: adjust default svg wave values & remove logging * refactor: tweak default SvgWave prop values * fix: svg preview with disconnected wallet * feat: show artwork gas fee estimation in user preferred currency * refactor: extract gas fee estimation logic to new component * cleanup: remove oceanWaves, remove d3 * icon visual weight tweak Co-authored-by: Luca Milanese Co-authored-by: Matthias Kretschmann --- .gitignore | 1 + content/publish/form.json | 2 +- package-lock.json | 706 +----------------- package.json | 1 - src/@utils/SvgWaves.ts | 190 +++++ src/@utils/bezier-spline.ts | 63 ++ src/@utils/nft.ts | 38 +- src/@utils/numbers.ts | 6 + src/@utils/oceanWaves.ts | 49 -- .../@shared/FormFields/Nft/TxFee.tsx | 67 ++ .../@shared/FormFields/Nft/index.module.css | 21 +- .../@shared/FormFields/Nft/index.tsx | 38 +- 12 files changed, 388 insertions(+), 794 deletions(-) create mode 100644 src/@utils/SvgWaves.ts create mode 100644 src/@utils/bezier-spline.ts delete mode 100644 src/@utils/oceanWaves.ts create mode 100644 src/components/@shared/FormFields/Nft/TxFee.tsx diff --git a/.gitignore b/.gitignore index 02737136c..0e8609497 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ coverage repo-metadata.json networks-metadata.json src/@types/subgraph +src/@types/apollo/ graphql.schema.json src/@types/graph.types.ts tsconfig.tsbuildinfo diff --git a/content/publish/form.json b/content/publish/form.json index 8863c4a85..7e2c85c89 100644 --- a/content/publish/form.json +++ b/content/publish/form.json @@ -6,7 +6,7 @@ "name": "nft", "label": "Data NFT", "type": "nft", - "help": "All metadata is stored on-chain in a newly deployed ERC-721 contract representing this asset, created with this name & symbol.", + "help": "All metadata is stored on-chain in a newly deployed ERC-721 contract representing this asset, created with this name, symbol, description and image.", "required": true }, { diff --git a/package-lock.json b/package-lock.json index d7b296861..8687a8cdd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,6 @@ "bignumber.js": "^9.0.2", "chart.js": "^3.7.0", "classnames": "^2.3.1", - "d3": "^7.3.0", "date-fns": "^2.28.0", "decimal.js": "^10.3.1", "dom-confetti": "^0.2.2", @@ -10570,6 +10569,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, "engines": { "node": ">= 10" } @@ -11018,376 +11018,6 @@ "type": "^1.0.1" } }, - "node_modules/d3": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.3.0.tgz", - "integrity": "sha512-MDRLJCMK232OJQRqGljQ/gCxtB8k3/sLKFjftMjzPB3nKVUODpdW9Rb3vcq7U8Ka5YKoZkAmp++Ur6I+6iNWIw==", - "dependencies": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "3", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-array": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.1.1.tgz", - "integrity": "sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ==", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz", - "integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-contour": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-3.0.1.tgz", - "integrity": "sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ==", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-delaunay": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", - "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", - "dependencies": { - "delaunator": "5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "dependencies": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json.js", - "csv2tsv": "bin/dsv2dsv.js", - "dsv2dsv": "bin/dsv2dsv.js", - "dsv2json": "bin/dsv2json.js", - "json2csv": "bin/json2dsv.js", - "json2dsv": "bin/json2dsv.js", - "json2tsv": "bin/json2dsv.js", - "tsv2csv": "bin/dsv2dsv.js", - "tsv2json": "bin/dsv2json.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "dependencies": { - "d3-dsv": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", - "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-hierarchy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.1.tgz", - "integrity": "sha512-LtAIu54UctRmhGKllleflmHalttH3zkfSi4NlKrTAoFKjC+AFBJohsCAdgCBYQwH0F8hIOGY89X1pPqAchlMkA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", - "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", - "dependencies": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", - "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "dependencies": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "d3-selection": "2 - 3" - } - }, - "node_modules/d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -11529,14 +11159,6 @@ "node": ">=0.10.0" } }, - "node_modules/delaunator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", - "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", - "dependencies": { - "robust-predicates": "^3.0.0" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -16968,17 +16590,6 @@ "node": ">=4" } }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/idna-uts46-hx": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", @@ -17257,14 +16868,6 @@ "node": ">= 0.4" } }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "engines": { - "node": ">=12" - } - }, "node_modules/interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", @@ -22738,11 +22341,6 @@ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", "dev": true }, - "node_modules/robust-predicates": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", - "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" - }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -22780,11 +22378,6 @@ "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" - }, "node_modules/rxjs": { "version": "6.6.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", @@ -36401,7 +35994,8 @@ "commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true }, "common-tags": { "version": "1.8.2", @@ -36778,269 +36372,6 @@ "type": "^1.0.1" } }, - "d3": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.3.0.tgz", - "integrity": "sha512-MDRLJCMK232OJQRqGljQ/gCxtB8k3/sLKFjftMjzPB3nKVUODpdW9Rb3vcq7U8Ka5YKoZkAmp++Ur6I+6iNWIw==", - "requires": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "3", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - } - }, - "d3-array": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.1.1.tgz", - "integrity": "sha512-33qQ+ZoZlli19IFiQx4QEpf2CBEayMRzhlisJHSCsSUbDXv6ZishqS1x7uFVClKG4Wr7rZVHvaAttoLow6GqdQ==", - "requires": { - "internmap": "1 - 2" - } - }, - "d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==" - }, - "d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - } - }, - "d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "requires": { - "d3-path": "1 - 3" - } - }, - "d3-color": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.0.1.tgz", - "integrity": "sha512-6/SlHkDOBLyQSJ1j1Ghs82OIUXpKWlR0hCsw0XrLSQhuUPuCSmLQ1QPH98vpnQxMUQM2/gfAkUEWsupVpd9JGw==" - }, - "d3-contour": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-3.0.1.tgz", - "integrity": "sha512-0Oc4D0KyhwhM7ZL0RMnfGycLN7hxHB8CMmwZ3+H26PWAG0ozNuYG5hXSDNgmP1SgJkQMrlG6cP20HoaSbvcJTQ==", - "requires": { - "d3-array": "2 - 3" - } - }, - "d3-delaunay": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", - "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", - "requires": { - "delaunator": "5" - } - }, - "d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" - }, - "d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - } - }, - "d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "requires": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - } - }, - "d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" - }, - "d3-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "requires": { - "d3-dsv": "1 - 3" - } - }, - "d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - } - }, - "d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" - }, - "d3-geo": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", - "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", - "requires": { - "d3-array": "2.5.0 - 3" - } - }, - "d3-hierarchy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.1.tgz", - "integrity": "sha512-LtAIu54UctRmhGKllleflmHalttH3zkfSi4NlKrTAoFKjC+AFBJohsCAdgCBYQwH0F8hIOGY89X1pPqAchlMkA==" - }, - "d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "requires": { - "d3-color": "1 - 3" - } - }, - "d3-path": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", - "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==" - }, - "d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==" - }, - "d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==" - }, - "d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==" - }, - "d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "requires": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - } - }, - "d3-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", - "requires": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - } - }, - "d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" - }, - "d3-shape": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", - "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", - "requires": { - "d3-path": "1 - 3" - } - }, - "d3-time": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", - "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", - "requires": { - "d3-array": "2 - 3" - } - }, - "d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "requires": { - "d3-time": "1 - 3" - } - }, - "d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" - }, - "d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "requires": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - } - }, - "d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "requires": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - } - }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -37140,14 +36471,6 @@ "is-descriptor": "^0.1.0" } }, - "delaunator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", - "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", - "requires": { - "robust-predicates": "^3.0.0" - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -41421,14 +40744,6 @@ "integrity": "sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ==", "dev": true }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, "idna-uts46-hx": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", @@ -41632,11 +40947,6 @@ "side-channel": "^1.0.4" } }, - "internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" - }, "interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", @@ -45886,11 +45196,6 @@ } } }, - "robust-predicates": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", - "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" - }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -45911,11 +45216,6 @@ "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" }, - "rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" - }, "rxjs": { "version": "6.6.7", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", diff --git a/package.json b/package.json index 2cc4923a3..17e3fc6ef 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "bignumber.js": "^9.0.2", "chart.js": "^3.7.0", "classnames": "^2.3.1", - "d3": "^7.3.0", "date-fns": "^2.28.0", "decimal.js": "^10.3.1", "dom-confetti": "^0.2.2", diff --git a/src/@utils/SvgWaves.ts b/src/@utils/SvgWaves.ts new file mode 100644 index 000000000..2d643d042 --- /dev/null +++ b/src/@utils/SvgWaves.ts @@ -0,0 +1,190 @@ +import { computeControlPoints } from './bezier-spline' +import { randomIntFromInterval } from './numbers' + +export interface WaveProperties { + width?: number + height?: number + color?: string + fill?: boolean + layerCount?: number + pointsPerLayer?: number + variance?: number + maxOpacity?: number + opacitySteps?: number +} + +class Point { + x: number + y: number + + constructor(x: number, y: number) { + this.x = Math.floor(x) + this.y = Math.floor(y) + } +} + +enum WaveColors { + Violet = '#e000cf', + Pink = '#ff4092', + Grey = '#8b98a9' +} + +export class SvgWaves { + properties: WaveProperties + layers: Point[][] + + static xmlns = 'http://www.w3.org/2000/svg' + + /** + * Helper function to get randomly generated WaveProperties + * These are generated with a focus on small file size for the svg + * meaning low character count. + * - width & height: default is 2 digits max (99) + * - color pink is selected per default + * - randomly decide if fill or stroke coloring should be used + * - create 4 layers with 4 - 5 points per layers + * -> results in random looking, yet small enough svgs + * - variance between 0.5 and 0.7 returns smooth & different results + * + * @returns new randomly generated WaveProperties + */ + static getProps(): WaveProperties { + return { + width: 99, + height: 99, + color: WaveColors.Pink, + fill: Math.random() < 0.5, // random true or false + layerCount: 4, + pointsPerLayer: randomIntFromInterval(3, 4), + variance: Math.random() * 0.2 + 0.5, // 0.5 - 0.7 + maxOpacity: 255, // 0xff + opacitySteps: 68 // 0x44 + } + } + + static generatePoint( + x: number, + y: number, + moveX: number, + moveY: number, + width: number + ): Point { + const varianceY = y - moveY / 2 + Math.random() * moveY + const varianceX = + x === 0 || x === width ? x : x - moveX / 2 + Math.random() * moveX + return new Point(varianceX, varianceY) + } + + constructor() { + this.properties = SvgWaves.getProps() + this.layers = this.generateLayers() + } + + generateLayers(): Point[][] { + const { width, height, layerCount, pointsPerLayer, variance } = + this.properties + + const cellWidth = width / pointsPerLayer + const cellHeight = height / layerCount + + // define movement constraints for point generation + // lower smoothness results in steeper curves in waves + const horizontalSmoothness = 0.5 + const moveX = cellWidth * variance * (1 - horizontalSmoothness) + const moveY = cellHeight * variance + + const layers = [] + for (let y = cellHeight; y < height; y += cellHeight) { + const points = [] + for (let x = 0; x <= width; x += cellWidth) { + points.push(SvgWaves.generatePoint(x, y, moveX, moveY, width)) + } + layers.push(points) + } + return layers + } + + generateSvg(): Element { + const svg = document.createElementNS(SvgWaves.xmlns, 'svg') + svg.setAttribute('width', this.properties.width.toString()) + svg.setAttribute('height', this.properties.height.toString()) + svg.setAttribute('fill', this.properties.fill ? undefined : 'transparent') + svg.setAttribute('xmlns', SvgWaves.xmlns) + + for (let i = 0; i < this.layers.length; i++) { + const path = this.generatePath( + this.layers[i], + this.getOpacityForLayer(i + 1) + ) + svg.appendChild(path) + } + return svg + } + + generatePath(points: Point[], opacity = 'ff'): Element { + const xPoints = points.map((p) => Math.floor(p.x)) + const yPoints = points.map((p) => Math.floor(p.y)) + + // get bezier control points + const xControlPoints = computeControlPoints(xPoints) + const yControlPoints = computeControlPoints(yPoints) + + // Should the path be closed & filled or stroke only + const closed = this.properties.fill + + // For closed paths define bottom corners as start & end point + const bottomLeftPoint = new Point(0, this.properties.height) + const bottomRightPoint = new Point( + this.properties.width, + this.properties.height + ) + const startPoint = closed ? bottomLeftPoint : points[0] + const endPoint = closed ? bottomRightPoint : points[points.length - 1] + + // start constructing the 'd' attribute for the + let path = `M${startPoint.x},${startPoint.y}` + + // if starting in bottom corner, move to start of wave first + if (closed) path += `L${xPoints[0]},${yPoints[0]}` + + // add path curves + for (let i = 0; i < xPoints.length - 1; i++) { + path += + `C${xControlPoints.p1[i]},${yControlPoints.p1[i]} ` + + `${xControlPoints.p2[i]},${yControlPoints.p2[i]} ` + + `${xPoints[i + 1]},${yPoints[i + 1]}` + } + + path = closed + ? `${path}L${endPoint.x},${endPoint.y}Z` // if closing in bottom corners move there and close + : `${path}AZ` // else just close the path + + // create the path element + const svgPath = document.createElementNS(SvgWaves.xmlns, 'path') + const colorStyle = closed ? 'fill' : 'stroke' + svgPath.setAttributeNS( + null, + colorStyle, + `${this.properties.color}${opacity}` + ) + svgPath.setAttributeNS(null, 'd', path) + + return svgPath + } + + getOpacityForLayer(layer: number): string { + const { layerCount, maxOpacity, opacitySteps } = this.properties + + const minOpacity = maxOpacity - (layerCount - 1) * opacitySteps + // calculate decimal opacity value for layer + const opacityDec = minOpacity + layer * opacitySteps + + // translate to hex string + return opacityDec.toString(16) + } + + setProps(properties: WaveProperties): void { + this.properties = { ...SvgWaves.getProps(), ...properties } + this.layers = this.generateLayers() + } +} diff --git a/src/@utils/bezier-spline.ts b/src/@utils/bezier-spline.ts new file mode 100644 index 000000000..915a468aa --- /dev/null +++ b/src/@utils/bezier-spline.ts @@ -0,0 +1,63 @@ +/* bezier-spline.js + * + * computes cubic bezier coefficients to generate a smooth + * line through specified points. couples with SVG graphics + * for interactive processing. + * + * For more info see: + * http://www.particleincell.com/2012/bezier-splines/ + * + * Lubos Brieda, Particle In Cell Consulting LLC, 2012 + * you may freely use this algorithm in your codes however where feasible + * please include a link/reference to the source article + */ + +/* computes control points given knots K, this is the brain of the operation */ +export function computeControlPoints(K: number[]) { + const p1 = [] + const p2 = [] + const n = K.length - 1 + + /* rhs vector */ + const a = [] + const b = [] + const c = [] + const r = [] + + /* left most segment */ + a[0] = 0 + b[0] = 2 + c[0] = 1 + r[0] = K[0] + 2 * K[1] + + /* internal segments */ + for (let i = 1; i < n - 1; i++) { + a[i] = 1 + b[i] = 4 + c[i] = 1 + r[i] = 4 * K[i] + 2 * K[i + 1] + } + + /* right segment */ + a[n - 1] = 2 + b[n - 1] = 7 + c[n - 1] = 0 + r[n - 1] = 8 * K[n - 1] + K[n] + + /* solves Ax=b with the Thomas algorithm (from Wikipedia) */ + for (let i = 1; i < n; i++) { + const m: number = a[i] / b[i - 1] + b[i] = b[i] - m * c[i - 1] + r[i] = r[i] - m * r[i - 1] + } + + p1[n - 1] = r[n - 1] / b[n - 1] + for (let i = n - 2; i >= 0; --i) p1[i] = (r[i] - c[i] * p1[i + 1]) / b[i] + + /* we have p1, now compute p2 */ + for (let i = 0; i < n - 1; i++) p2[i] = 2 * K[i + 1] - p1[i + 1] + + p2[n - 1] = 0.5 * (K[n] + p1[n - 1]) + + return { p1: p1.map((p) => Math.floor(p)), p2: p2.map((p) => Math.floor(p)) } +} diff --git a/src/@utils/nft.ts b/src/@utils/nft.ts index 99b086f0d..62ae06711 100644 --- a/src/@utils/nft.ts +++ b/src/@utils/nft.ts @@ -1,4 +1,4 @@ -import { renderStaticWaves } from './oceanWaves' +import { SvgWaves } from './SvgWaves' // https://docs.opensea.io/docs/metadata-standards export interface NftMetadata { @@ -21,6 +21,7 @@ function encodeSvg(svgString: string): string { ? '', '/>') .replace(/"/g, "'") .replace(/%/g, '%25') .replace(/#/g, '%23') @@ -32,41 +33,40 @@ function encodeSvg(svgString: string): string { } export function generateNftMetadata(): NftMetadata { - // TODO: crop image properly in the end as generated SVG waves are a super-wide image, - // and add a filled background deciding on either black or white. - const image = renderStaticWaves() - // const image = new XMLSerializer().serializeToString(waves) - // const image = `` + const waves = new SvgWaves() + const svg = waves.generateSvg() + + // TODO: figure out if also image URI needs base64 encoding + // e.g. 'data:image/svg+xml;base64,' + // generated SVG embedded as 'data:image/svg+xml' and encoded characters + const imageData = `data:image/svg+xml,${encodeSvg(svg.outerHTML)}` const newNft: NftMetadata = { - name: 'Ocean Asset v4 NFT', + name: 'Ocean Asset NFT', symbol: 'OCEAN-NFT', description: `This NFT represents an asset in the Ocean Protocol v4 ecosystem.`, // TODO: ideally this includes the final DID external_url: 'https://market.oceanprotocol.com', background_color: '141414', // dark background - // TODO: figure out if also image URI needs base64 encoding - // generated SVG embedded as 'data:image/svg+xml' and encoded characters - image_data: `data:image/svg+xml,${encodeSvg(image)}` - // generated SVG embedded as 'data:image/svg+xml;base64' - // image: `data:image/svg+xml;base64,${window?.btoa(image)}` - // image: `data:image/svg+xml;base64,${Buffer.from(image).toString('base64')}` + image_data: imageData } return newNft } export function generateNftCreateData(nftMetadata: NftMetadata): any { + // TODO: figure out if Buffer.from method is working in browser in final build + // as BTOA is deprecated. + // tokenURI: window?.btoa(JSON.stringify(nftMetadata)) + const encodedMetadata = Buffer.from(JSON.stringify(nftMetadata)).toString( + 'base64' + ) + const nftCreateData = { name: nftMetadata.name, symbol: nftMetadata.symbol, templateIndex: 1, - // Gas estimation fails if we add our huge tokenURI - tokenURI: '{url:"https://coolImage.com, name: "just for test"}' - // TODO: figure out if Buffer.from method is working in browser in final build - // as BTOA is deprecated. - // tokenURI: window?.btoa(JSON.stringify(nftMetadata)) - // tokenURI: Buffer.from(JSON.stringify(nftMetadata)).toString('base64') // should end up as data:application/json;base64 + tokenURI: `data:application/json;base64,${encodedMetadata}` } return nftCreateData diff --git a/src/@utils/numbers.ts b/src/@utils/numbers.ts index fe19da2ff..55a3bc027 100644 --- a/src/@utils/numbers.ts +++ b/src/@utils/numbers.ts @@ -24,3 +24,9 @@ export function compareAsBN(balance: string, price: string): boolean { return false } } + +// Random number from interval +export function randomIntFromInterval(min: number, max: number): number { + // min and max are included + return Math.floor(Math.random() * (max - min + 1) + min) +} diff --git a/src/@utils/oceanWaves.ts b/src/@utils/oceanWaves.ts deleted file mode 100644 index 7c37cfdd0..000000000 --- a/src/@utils/oceanWaves.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as d3 from 'd3' - -/* - * Ocean Protocol D3 waves - * https://oceanprotocol.com/art - * Based off of Bostock's Circle Wave - * https://bl.ocks.org/mbostock/2d466ec3417722e3568cd83fc35338e3 - */ -export function renderStaticWaves(): string { - const svg = d3.create('svg') - const width = 1000 - const height = 250 - const x = d3.scaleLinear().range([0, width]) - const angles = d3.range(Math.random(), 4 * Math.PI, Math.PI / 20) - - const path = svg - // .append('rect') - // .attr('fill', '#fff') - // .attr('width', '100%') - // .attr('height', '100%') - .append('g') - .attr('transform', `translate(${width / -4}, ${height / 2})`) - .attr('fill', 'none') - .attr('stroke-width', 2) - .selectAll('path') - .data(['#FF4092', '#E000CF', '#8B98A9', '#E2E2E2']) - .enter() - .append('path') - .attr('stroke', (d) => d) - .style('mix-blend-mode', 'darken') - .datum((d, i) => { - return d3 - .line() - .curve(d3.curveBasisOpen) - .x((angles: any) => x(angles / 4)) - .y((angles: any) => { - const t = d3.now() / 3000 - return ( - Math.cos(angles * 8 - (i * 2 * Math.PI) / 10 + t) * - Math.pow((2 + Math.cos(angles - t)) / 2, 4) * - 15 - ) - }) - }) - - path.attr('d', (d) => d(angles as any)) - - return `${svg.node().innerHTML}` -} diff --git a/src/components/@shared/FormFields/Nft/TxFee.tsx b/src/components/@shared/FormFields/Nft/TxFee.tsx new file mode 100644 index 000000000..4937ac21e --- /dev/null +++ b/src/components/@shared/FormFields/Nft/TxFee.tsx @@ -0,0 +1,67 @@ +import React, { ReactElement, useEffect, useState } from 'react' +import { usePrices } from '@context/Prices' +import { useWeb3 } from '@context/Web3' +import useNftFactory from '@hooks/contracts/useNftFactory' +import { NftFactory } from '@oceanprotocol/lib' +import Conversion from '@shared/Price/Conversion' +import { generateNftCreateData, NftMetadata } from '@utils/nft' + +const getEstGasFee = async ( + address: string, + nftFactory: NftFactory, + nftMetadata: NftMetadata, + ethToOceanConversionRate: number +): Promise => { + if (!address || !nftFactory || !nftMetadata || !ethToOceanConversionRate) + return + + const { web3 } = nftFactory + const nft = generateNftCreateData(nftMetadata) + + const gasPrice = await web3.eth.getGasPrice() + const gasLimit = await nftFactory?.estGasCreateNFT(address, nft) + const gasFeeEth = web3.utils.fromWei( + (+gasPrice * +gasLimit).toString(), + 'ether' + ) + const gasFeeOcean = (+gasFeeEth / +ethToOceanConversionRate).toString() + return gasFeeOcean +} + +export default function TxFee({ + nftMetadata +}: { + nftMetadata: NftMetadata +}): ReactElement { + const { accountId } = useWeb3() + const { prices } = usePrices() + const nftFactory = useNftFactory() + const [gasFee, setGasFee] = useState('') + + useEffect(() => { + const calculateGasFee = async () => + setGasFee( + await getEstGasFee( + accountId, + nftFactory, + nftMetadata, + (prices as any)?.eth + ) + ) + calculateGasFee() + }, [accountId, nftFactory, nftMetadata, prices]) + + return gasFee ? ( +

+ Gas fee estimation for this artwork + +

+ ) : accountId ? ( +

+ An error occurred while estimating the gas fee for this artwork, please + try again. +

+ ) : ( +

Please connect your wallet to get a gas fee estimate for this artwork

+ ) +} diff --git a/src/components/@shared/FormFields/Nft/index.module.css b/src/components/@shared/FormFields/Nft/index.module.css index dedd36f58..01ad64eda 100644 --- a/src/components/@shared/FormFields/Nft/index.module.css +++ b/src/components/@shared/FormFields/Nft/index.module.css @@ -21,14 +21,23 @@ position: relative; } -.refresh { +.actions { position: absolute; - right: calc(var(--spacer) / 4); - bottom: calc(var(--spacer) / 4); + left: 0; + bottom: 0; + padding: 0 calc(var(--spacer) / 4); + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; +} + +.actions svg { + margin: 0; + fill: var(--font-color-text); } .refresh svg { - fill: var(--brand-pink); - width: var(--font-size-mini); - height: var(--font-size-mini); + transform: scale(0.9); + transform-origin: center; } diff --git a/src/components/@shared/FormFields/Nft/index.tsx b/src/components/@shared/FormFields/Nft/index.tsx index 109e22bf2..61f6bd6ff 100644 --- a/src/components/@shared/FormFields/Nft/index.tsx +++ b/src/components/@shared/FormFields/Nft/index.tsx @@ -5,35 +5,43 @@ import { useField } from 'formik' import React, { ReactElement, useEffect } from 'react' import Refresh from '@images/refresh.svg' import styles from './index.module.css' +import Tooltip from '@shared/atoms/Tooltip' +import TxFee from './TxFee' export default function Nft(props: InputProps): ReactElement { const [field, meta, helpers] = useField(props.name) + const refreshNftMetadata = () => { + const nftMetadata = generateNftMetadata() + helpers.setValue({ ...nftMetadata }) + } + // Generate on first mount useEffect(() => { if (field.value?.name !== '') return - const nftOptions = generateNftMetadata() - helpers.setValue({ ...nftOptions }) + refreshNftMetadata() }, [field.value?.name]) return (
- +
+ } /> + +