chore: added sankey diagram

This commit is contained in:
Andre Henriques 2024-10-07 13:40:36 +01:00
parent dd9d174449
commit 82b1a46617
9 changed files with 1299 additions and 11 deletions

View File

@ -81,6 +81,7 @@ async function startup() {
browser.menus.onClicked.addListener(async function (e, tab) {
if (e.menuItemId === "mark-page") {
console.log("set mark-page", tab.id)
await browser.storage.local.set({
windows: tab.id,
});

1
site/.dockerignore Normal file
View File

@ -0,0 +1 @@
node_modules/

View File

@ -30,10 +30,11 @@
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"typescript-eslint": "^8.0.0-alpha.20",
"vite": "^5.0.3"
"vite": "^5.0.3",
"@sveltejs/adapter-static": "^3.0.5",
"@types/d3": "^7.4.3",
"d3": "^7.9.0",
"d3-sankey": "^0.12.3"
},
"type": "module",
"dependencies": {
"@sveltejs/adapter-static": "^3.0.5"
}
"type": "module"
}

View File

@ -7,26 +7,34 @@ settings:
importers:
.:
dependencies:
'@sveltejs/adapter-static':
specifier: ^3.0.5
version: 3.0.5(@sveltejs/kit@2.5.18(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.174)(vite@5.3.3))(svelte@5.0.0-next.174)(vite@5.3.3))
devDependencies:
'@sveltejs/adapter-auto':
specifier: ^3.0.0
version: 3.2.2(@sveltejs/kit@2.5.18(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.174)(vite@5.3.3))(svelte@5.0.0-next.174)(vite@5.3.3))
'@sveltejs/adapter-static':
specifier: ^3.0.5
version: 3.0.5(@sveltejs/kit@2.5.18(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.174)(vite@5.3.3))(svelte@5.0.0-next.174)(vite@5.3.3))
'@sveltejs/kit':
specifier: ^2.0.0
version: 2.5.18(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.174)(vite@5.3.3))(svelte@5.0.0-next.174)(vite@5.3.3)
'@sveltejs/vite-plugin-svelte':
specifier: ^3.0.0
version: 3.1.1(svelte@5.0.0-next.174)(vite@5.3.3)
'@types/d3':
specifier: ^7.4.3
version: 7.4.3
'@types/eslint':
specifier: ^8.56.7
version: 8.56.10
autoprefixer:
specifier: ^10.4.19
version: 10.4.19(postcss@8.4.39)
d3:
specifier: ^7.9.0
version: 7.9.0
d3-sankey:
specifier: ^0.12.3
version: 0.12.3
eslint:
specifier: ^9.0.0
version: 9.6.0
@ -410,12 +418,108 @@ packages:
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
'@types/d3-array@3.2.1':
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
'@types/d3-axis@3.0.6':
resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==}
'@types/d3-brush@3.0.6':
resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==}
'@types/d3-chord@3.0.6':
resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==}
'@types/d3-color@3.1.3':
resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
'@types/d3-contour@3.0.6':
resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==}
'@types/d3-delaunay@6.0.4':
resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==}
'@types/d3-dispatch@3.0.6':
resolution: {integrity: sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==}
'@types/d3-drag@3.0.7':
resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
'@types/d3-dsv@3.0.7':
resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==}
'@types/d3-ease@3.0.2':
resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
'@types/d3-fetch@3.0.7':
resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==}
'@types/d3-force@3.0.10':
resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==}
'@types/d3-format@3.0.4':
resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==}
'@types/d3-geo@3.1.0':
resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==}
'@types/d3-hierarchy@3.1.7':
resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==}
'@types/d3-interpolate@3.0.4':
resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
'@types/d3-path@3.1.0':
resolution: {integrity: sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==}
'@types/d3-polygon@3.0.2':
resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==}
'@types/d3-quadtree@3.0.6':
resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==}
'@types/d3-random@3.0.3':
resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==}
'@types/d3-scale-chromatic@3.0.3':
resolution: {integrity: sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==}
'@types/d3-scale@4.0.8':
resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==}
'@types/d3-selection@3.0.10':
resolution: {integrity: sha512-cuHoUgS/V3hLdjJOLTT691+G2QoqAjCVLmr4kJXR4ha56w1Zdu8UUQ5TxLRqudgNjwXeQxKMq4j+lyf9sWuslg==}
'@types/d3-shape@3.1.6':
resolution: {integrity: sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==}
'@types/d3-time-format@4.0.3':
resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==}
'@types/d3-time@3.0.3':
resolution: {integrity: sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==}
'@types/d3-timer@3.0.2':
resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
'@types/d3-transition@3.0.8':
resolution: {integrity: sha512-ew63aJfQ/ms7QQ4X7pk5NxQ9fZH/z+i24ZfJ6tJSfqxJMrYLiK01EAs2/Rtw/JreGUsS3pLPNV644qXFGnoZNQ==}
'@types/d3-zoom@3.0.8':
resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
'@types/d3@7.4.3':
resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==}
'@types/eslint@8.56.10':
resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==}
'@types/estree@1.0.5':
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
'@types/geojson@7946.0.14':
resolution: {integrity: sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
@ -599,6 +703,10 @@ packages:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
commander@7.2.0:
resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
engines: {node: '>= 10'}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@ -615,6 +723,145 @@ packages:
engines: {node: '>=4'}
hasBin: true
d3-array@2.12.1:
resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==}
d3-array@3.2.4:
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
engines: {node: '>=12'}
d3-axis@3.0.0:
resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
engines: {node: '>=12'}
d3-brush@3.0.0:
resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
engines: {node: '>=12'}
d3-chord@3.0.1:
resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
engines: {node: '>=12'}
d3-color@3.1.0:
resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
engines: {node: '>=12'}
d3-contour@4.0.2:
resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==}
engines: {node: '>=12'}
d3-delaunay@6.0.4:
resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
engines: {node: '>=12'}
d3-dispatch@3.0.1:
resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
engines: {node: '>=12'}
d3-drag@3.0.0:
resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
engines: {node: '>=12'}
d3-dsv@3.0.1:
resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
engines: {node: '>=12'}
hasBin: true
d3-ease@3.0.1:
resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
engines: {node: '>=12'}
d3-fetch@3.0.1:
resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
engines: {node: '>=12'}
d3-force@3.0.0:
resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
engines: {node: '>=12'}
d3-format@3.1.0:
resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
engines: {node: '>=12'}
d3-geo@3.1.1:
resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==}
engines: {node: '>=12'}
d3-hierarchy@3.1.2:
resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
engines: {node: '>=12'}
d3-interpolate@3.0.1:
resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
engines: {node: '>=12'}
d3-path@1.0.9:
resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==}
d3-path@3.1.0:
resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
engines: {node: '>=12'}
d3-polygon@3.0.1:
resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
engines: {node: '>=12'}
d3-quadtree@3.0.1:
resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
engines: {node: '>=12'}
d3-random@3.0.1:
resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
engines: {node: '>=12'}
d3-sankey@0.12.3:
resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==}
d3-scale-chromatic@3.1.0:
resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
engines: {node: '>=12'}
d3-scale@4.0.2:
resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
engines: {node: '>=12'}
d3-selection@3.0.0:
resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
engines: {node: '>=12'}
d3-shape@1.3.7:
resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==}
d3-shape@3.2.0:
resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
engines: {node: '>=12'}
d3-time-format@4.1.0:
resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
engines: {node: '>=12'}
d3-time@3.1.0:
resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
engines: {node: '>=12'}
d3-timer@3.0.1:
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
engines: {node: '>=12'}
d3-transition@3.0.1:
resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
engines: {node: '>=12'}
peerDependencies:
d3-selection: 2 - 3
d3-zoom@3.0.0:
resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
engines: {node: '>=12'}
d3@7.9.0:
resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==}
engines: {node: '>=12'}
debug@4.3.5:
resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
engines: {node: '>=6.0'}
@ -631,6 +878,9 @@ packages:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
delaunator@5.0.1:
resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==}
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
@ -855,6 +1105,10 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
iconv-lite@0.6.3:
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
engines: {node: '>=0.10.0'}
ignore@5.3.1:
resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==}
engines: {node: '>= 4'}
@ -877,6 +1131,13 @@ packages:
inherits@2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
internmap@1.0.1:
resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==}
internmap@2.0.3:
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
engines: {node: '>=12'}
is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
@ -1216,6 +1477,9 @@ packages:
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
robust-predicates@3.0.2:
resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
rollup@4.18.0:
resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==}
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
@ -1224,10 +1488,16 @@ packages:
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
rw@1.3.3:
resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
sade@1.8.1:
resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
engines: {node: '>=6'}
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
sander@0.5.1:
resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==}
@ -1764,6 +2034,123 @@ snapshots:
'@types/cookie@0.6.0': {}
'@types/d3-array@3.2.1': {}
'@types/d3-axis@3.0.6':
dependencies:
'@types/d3-selection': 3.0.10
'@types/d3-brush@3.0.6':
dependencies:
'@types/d3-selection': 3.0.10
'@types/d3-chord@3.0.6': {}
'@types/d3-color@3.1.3': {}
'@types/d3-contour@3.0.6':
dependencies:
'@types/d3-array': 3.2.1
'@types/geojson': 7946.0.14
'@types/d3-delaunay@6.0.4': {}
'@types/d3-dispatch@3.0.6': {}
'@types/d3-drag@3.0.7':
dependencies:
'@types/d3-selection': 3.0.10
'@types/d3-dsv@3.0.7': {}
'@types/d3-ease@3.0.2': {}
'@types/d3-fetch@3.0.7':
dependencies:
'@types/d3-dsv': 3.0.7
'@types/d3-force@3.0.10': {}
'@types/d3-format@3.0.4': {}
'@types/d3-geo@3.1.0':
dependencies:
'@types/geojson': 7946.0.14
'@types/d3-hierarchy@3.1.7': {}
'@types/d3-interpolate@3.0.4':
dependencies:
'@types/d3-color': 3.1.3
'@types/d3-path@3.1.0': {}
'@types/d3-polygon@3.0.2': {}
'@types/d3-quadtree@3.0.6': {}
'@types/d3-random@3.0.3': {}
'@types/d3-scale-chromatic@3.0.3': {}
'@types/d3-scale@4.0.8':
dependencies:
'@types/d3-time': 3.0.3
'@types/d3-selection@3.0.10': {}
'@types/d3-shape@3.1.6':
dependencies:
'@types/d3-path': 3.1.0
'@types/d3-time-format@4.0.3': {}
'@types/d3-time@3.0.3': {}
'@types/d3-timer@3.0.2': {}
'@types/d3-transition@3.0.8':
dependencies:
'@types/d3-selection': 3.0.10
'@types/d3-zoom@3.0.8':
dependencies:
'@types/d3-interpolate': 3.0.4
'@types/d3-selection': 3.0.10
'@types/d3@7.4.3':
dependencies:
'@types/d3-array': 3.2.1
'@types/d3-axis': 3.0.6
'@types/d3-brush': 3.0.6
'@types/d3-chord': 3.0.6
'@types/d3-color': 3.1.3
'@types/d3-contour': 3.0.6
'@types/d3-delaunay': 6.0.4
'@types/d3-dispatch': 3.0.6
'@types/d3-drag': 3.0.7
'@types/d3-dsv': 3.0.7
'@types/d3-ease': 3.0.2
'@types/d3-fetch': 3.0.7
'@types/d3-force': 3.0.10
'@types/d3-format': 3.0.4
'@types/d3-geo': 3.1.0
'@types/d3-hierarchy': 3.1.7
'@types/d3-interpolate': 3.0.4
'@types/d3-path': 3.1.0
'@types/d3-polygon': 3.0.2
'@types/d3-quadtree': 3.0.6
'@types/d3-random': 3.0.3
'@types/d3-scale': 4.0.8
'@types/d3-scale-chromatic': 3.0.3
'@types/d3-selection': 3.0.10
'@types/d3-shape': 3.1.6
'@types/d3-time': 3.0.3
'@types/d3-time-format': 4.0.3
'@types/d3-timer': 3.0.2
'@types/d3-transition': 3.0.8
'@types/d3-zoom': 3.0.8
'@types/eslint@8.56.10':
dependencies:
'@types/estree': 1.0.5
@ -1771,6 +2158,8 @@ snapshots:
'@types/estree@1.0.5': {}
'@types/geojson@7946.0.14': {}
'@types/json-schema@7.0.15': {}
'@types/pug@2.0.10': {}
@ -1971,6 +2360,8 @@ snapshots:
commander@4.1.1: {}
commander@7.2.0: {}
concat-map@0.0.1: {}
cookie@0.6.0: {}
@ -1983,6 +2374,173 @@ snapshots:
cssesc@3.0.0: {}
d3-array@2.12.1:
dependencies:
internmap: 1.0.1
d3-array@3.2.4:
dependencies:
internmap: 2.0.3
d3-axis@3.0.0: {}
d3-brush@3.0.0:
dependencies:
d3-dispatch: 3.0.1
d3-drag: 3.0.0
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
d3-chord@3.0.1:
dependencies:
d3-path: 3.1.0
d3-color@3.1.0: {}
d3-contour@4.0.2:
dependencies:
d3-array: 3.2.4
d3-delaunay@6.0.4:
dependencies:
delaunator: 5.0.1
d3-dispatch@3.0.1: {}
d3-drag@3.0.0:
dependencies:
d3-dispatch: 3.0.1
d3-selection: 3.0.0
d3-dsv@3.0.1:
dependencies:
commander: 7.2.0
iconv-lite: 0.6.3
rw: 1.3.3
d3-ease@3.0.1: {}
d3-fetch@3.0.1:
dependencies:
d3-dsv: 3.0.1
d3-force@3.0.0:
dependencies:
d3-dispatch: 3.0.1
d3-quadtree: 3.0.1
d3-timer: 3.0.1
d3-format@3.1.0: {}
d3-geo@3.1.1:
dependencies:
d3-array: 3.2.4
d3-hierarchy@3.1.2: {}
d3-interpolate@3.0.1:
dependencies:
d3-color: 3.1.0
d3-path@1.0.9: {}
d3-path@3.1.0: {}
d3-polygon@3.0.1: {}
d3-quadtree@3.0.1: {}
d3-random@3.0.1: {}
d3-sankey@0.12.3:
dependencies:
d3-array: 2.12.1
d3-shape: 1.3.7
d3-scale-chromatic@3.1.0:
dependencies:
d3-color: 3.1.0
d3-interpolate: 3.0.1
d3-scale@4.0.2:
dependencies:
d3-array: 3.2.4
d3-format: 3.1.0
d3-interpolate: 3.0.1
d3-time: 3.1.0
d3-time-format: 4.1.0
d3-selection@3.0.0: {}
d3-shape@1.3.7:
dependencies:
d3-path: 1.0.9
d3-shape@3.2.0:
dependencies:
d3-path: 3.1.0
d3-time-format@4.1.0:
dependencies:
d3-time: 3.1.0
d3-time@3.1.0:
dependencies:
d3-array: 3.2.4
d3-timer@3.0.1: {}
d3-transition@3.0.1(d3-selection@3.0.0):
dependencies:
d3-color: 3.1.0
d3-dispatch: 3.0.1
d3-ease: 3.0.1
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-timer: 3.0.1
d3-zoom@3.0.0:
dependencies:
d3-dispatch: 3.0.1
d3-drag: 3.0.0
d3-interpolate: 3.0.1
d3-selection: 3.0.0
d3-transition: 3.0.1(d3-selection@3.0.0)
d3@7.9.0:
dependencies:
d3-array: 3.2.4
d3-axis: 3.0.0
d3-brush: 3.0.0
d3-chord: 3.0.1
d3-color: 3.1.0
d3-contour: 4.0.2
d3-delaunay: 6.0.4
d3-dispatch: 3.0.1
d3-drag: 3.0.0
d3-dsv: 3.0.1
d3-ease: 3.0.1
d3-fetch: 3.0.1
d3-force: 3.0.0
d3-format: 3.1.0
d3-geo: 3.1.1
d3-hierarchy: 3.1.2
d3-interpolate: 3.0.1
d3-path: 3.1.0
d3-polygon: 3.0.1
d3-quadtree: 3.0.1
d3-random: 3.0.1
d3-scale: 4.0.2
d3-scale-chromatic: 3.1.0
d3-selection: 3.0.0
d3-shape: 3.2.0
d3-time: 3.1.0
d3-time-format: 4.1.0
d3-timer: 3.0.1
d3-transition: 3.0.1(d3-selection@3.0.0)
d3-zoom: 3.0.0
debug@4.3.5:
dependencies:
ms: 2.1.2
@ -1991,6 +2549,10 @@ snapshots:
deepmerge@4.3.1: {}
delaunator@5.0.1:
dependencies:
robust-predicates: 3.0.2
dequal@2.0.3: {}
detect-indent@6.1.0: {}
@ -2262,6 +2824,10 @@ snapshots:
dependencies:
function-bind: 1.1.2
iconv-lite@0.6.3:
dependencies:
safer-buffer: 2.1.2
ignore@5.3.1: {}
import-fresh@3.3.0:
@ -2280,6 +2846,10 @@ snapshots:
inherits@2.0.4: {}
internmap@1.0.1: {}
internmap@2.0.3: {}
is-binary-path@2.1.0:
dependencies:
binary-extensions: 2.3.0
@ -2545,6 +3115,8 @@ snapshots:
dependencies:
glob: 7.2.3
robust-predicates@3.0.2: {}
rollup@4.18.0:
dependencies:
'@types/estree': 1.0.5
@ -2571,10 +3143,14 @@ snapshots:
dependencies:
queue-microtask: 1.2.3
rw@1.3.3: {}
sade@1.8.1:
dependencies:
mri: 1.2.0
safer-buffer@2.1.2: {}
sander@0.5.1:
dependencies:
es6-promise: 3.3.1

View File

@ -1,6 +1,8 @@
import type { Flair } from './FlairStore.svelte';
import { post } from './utils';
export type AsEnum<T> = T[keyof T];
export const ApplicationStatus = Object.freeze({
ToApply: 0,
WorkingOnIt: 1,
@ -37,7 +39,7 @@ export type Application = {
user_id: string;
extra_data: string;
payrange: string;
status: number;
status: AsEnum<typeof ApplicationStatus>;
recruiter: string;
company: string;
message: string;

View File

@ -12,6 +12,9 @@
<button class="text-secudanry hover:text-primary px-2" onclick={() => goto('/flair')}>
Flair
</button>
<button class="text-secudanry hover:text-primary px-2" onclick={() => goto('/graphs')}>
Graphs
</button>
<div class="flex-grow"></div>
<div class="text-secudanry px-2">
{userStore.user.username}

View File

@ -0,0 +1,244 @@
<script lang="ts">
import type { Application, AsEnum } from '$lib/ApplicationsStore.svelte';
import { ApplicationStatus, ApplicationStatusMaping } from '$lib/ApplicationsStore.svelte';
import HasUser from '$lib/HasUser.svelte';
import { post } from '$lib/utils';
import NavBar from '../NavBar.svelte';
import * as d3 from 'd3';
import { sankey as mySankey } from './sankey';
let applications: Application[] = $state([]);
let chartDiv: HTMLDivElement;
async function getData() {
try {
applications = await post('application/list', {});
chartDiv.innerHTML = '';
type NodeType =
| AsEnum<typeof ApplicationStatus>
| 'Linkedin'
| 'Glassdoor'
| 'Unknown Source'
| 'Applications';
let nodeTypes: Record<NodeType, number> = {
[ApplicationStatus.ToApply]: 0,
[ApplicationStatus.WorkingOnIt]: 0,
[ApplicationStatus.Ignore]: 0,
[ApplicationStatus.ApplyedButSaidNo]: 0,
[ApplicationStatus.Expired]: 0,
[ApplicationStatus.Applyed]: 0,
Linkedin: 0,
Glassdoor: 0,
'Unknown Source': 0,
Applications: applications.length
};
const baseLinks: { source: NodeType; target: NodeType; value: 0 | 1 }[] = [
{ source: 'Linkedin', target: 'Applications', value: 0 },
{ source: 'Glassdoor', target: 'Applications', value: 0 },
{ source: 'Unknown Source', target: 'Applications', value: 0 },
{ source: 'Applications', target: ApplicationStatus.ToApply, value: 1 },
{ source: 'Applications', target: ApplicationStatus.Ignore, value: 1 },
{ source: 'Applications', target: ApplicationStatus.Expired, value: 1 },
{ source: 'Applications', target: ApplicationStatus.WorkingOnIt, value: 1 },
{ source: 'Applications', target: ApplicationStatus.Applyed, value: 1 },
{
source: ApplicationStatus.Applyed,
target: ApplicationStatus.ApplyedButSaidNo,
value: 1
}
];
applications.forEach((a) => {
if (a.url.includes('linkedin')) {
nodeTypes['Linkedin'] += 1;
} else if (a.url.includes('glassdoor')) {
nodeTypes['Glassdoor'] += 1;
} else {
nodeTypes['Unknown Source'] += 1;
}
nodeTypes[a.status] += 1;
if (a.status === ApplicationStatus.ApplyedButSaidNo) {
nodeTypes[ApplicationStatus.Applyed] += 1;
}
});
let inNodes: string[] = [];
let nodes = (Object.keys(nodeTypes) as (keyof typeof nodeTypes)[])
.filter((a) => nodeTypes[a] > 0)
.map((a, i) => {
inNodes.push(`${a}`);
const base = {
value: nodeTypes[a],
originalValue: a,
id: '',
color: 'red',
index: i
};
if (Number.isNaN(Number(a))) {
base.id = a as string;
} else {
base.id = ApplicationStatusMaping[a as AsEnum<typeof ApplicationStatus>];
}
return base;
});
const links = baseLinks
.filter(
(link) =>
inNodes.includes(`${link.source}`) && inNodes.includes(`${link.target}`)
)
.map((link) => {
const source = inNodes.indexOf(`${link.source}`);
const target = inNodes.indexOf(`${link.target}`);
return {
source: source,
target: target,
value: [nodes[source], nodes[target]][link.value].value
};
});
const bounding = chartDiv.getBoundingClientRect();
let sankey = mySankey()
.nodeWidth(20)
.nodePadding(10)
.size([bounding.width, bounding.height]);
let path = sankey.link();
sankey.nodes(nodes).links(links).layout(32);
const svg = d3
.select(chartDiv)
.append('svg')
.attr('width', bounding.width)
.attr('height', bounding.height)
.attr('viewBox', [0, 0, bounding.width, bounding.height])
.attr('style', 'max-width: 100%; height: auto; height: intrinsic;');
let color = d3.schemeSpectral[nodes.length];
// add in the links
var link = svg
.append('g')
.selectAll('.link')
.data(
links as ((typeof links)[0] & {
dy: number;
source: (typeof nodes)[0];
target: (typeof nodes)[0];
})[]
)
.enter()
.append('path')
.attr('class', 'link')
.attr('d', path)
.style('stroke-width', function (d) {
return Math.max(1, d.dy);
})
.sort(function (a, b) {
return b.dy - a.dy;
});
// add the link titles
link.append('title').text(function (d) {
return d.source.id + ' → ' + d.target.id + '\n' + d.value;
});
const node = svg
.append('g')
.selectAll('.node')
.data(
nodes as ((typeof nodes)[0] & {
x: number;
y: number;
dy: number;
dx: number;
index: number;
})[]
)
.enter()
.append('g')
.attr('class', 'node')
.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
node.append('rect')
.attr('height', function (d) {
return d.dy;
})
.attr('width', sankey.getNodeWidth())
.style('fill', function (d) {
return color[d.index];
})
.style('stroke', (d) => {
return d3.rgb(color[d.index]).darker(2).toString();
})
.append('title')
.text(function (d) {
return d.id + '\n' + d.value;
});
node.append('text')
.attr('x', -6)
.attr('y', function (d) {
return d.dy / 2;
})
.attr('dy', '.35em')
.attr('text-anchor', 'end')
.attr('transform', null)
.text(function (d) {
return `${d.id} (${d.value})`;
})
.filter(function (d) {
return d.x < bounding.width / 2;
})
.attr('x', 6 + sankey.getNodeWidth())
.attr('text-anchor', 'start');
} catch (e) {
console.log('TODO, inform the user', e);
}
}
$effect(() => {
getData();
});
</script>
<HasUser redirect="/cv">
<div class="flex flex-col h-[100vh]">
<NavBar />
<div class="flex flex-grow p-5">
<div class="bg-white p-3 rounded-lg" style="width: 100%; height: 100%">
<div bind:this={chartDiv} style="width: 100%; height: 100%;"></div>
</div>
</div>
</div>
</HasUser>
<style>
:global(.node rect) {
cursor: move;
fill-opacity: 0.9;
shape-rendering: crispEdges;
}
:global(.node text) {
pointer-events: none;
text-shadow: 0 1px 0 #fff;
}
:global(.link) {
fill: none;
stroke: #000;
stroke-opacity: 0.2;
}
</style>

View File

@ -0,0 +1,455 @@
import * as d3 from 'd3';
export type SankeyLink<Link> = ((d: Link) => string) & { curvature: (n: number) => SankeyLink<Link> };
export type Sankey<Node, Link> = {
nodeWidth: (w: number) => Sankey<Node, Link>;
getNodeWidth: () => number;
nodePadding: (padding: number) => Sankey<Node, Link>;
nodes: (nodes: Node[]) => Sankey<Node, Link>;
links: (links: Link[]) => Sankey<Node, Link>;
size: (size: number[]) => Sankey<Node, Link>;
layout: (iterations: number) => Sankey<Node, Link>;
relayout: () => Sankey<Node, Link>;
link: () => SankeyLink<Link>;
};
export function sankey<Node = any, Link = any>(): Sankey<Node, Link> {
let sankey = {},
nodeWidth = 24,
nodePadding = 8,
size = [1, 1],
nodes = [],
links = [];
sankey.getNodeWidth = () => nodeWidth;
sankey.nodeWidth = function(w) {
nodeWidth = +w;
return sankey;
};
sankey.nodePadding = function(_) {
if (!arguments.length) return nodePadding;
nodePadding = +_;
return sankey;
};
sankey.nodes = function(_) {
if (!arguments.length) return nodes;
nodes = _;
return sankey;
};
sankey.links = function(_) {
if (!arguments.length) return links;
links = _;
return sankey;
};
sankey.size = function(_) {
if (!arguments.length) return size;
size = _;
return sankey;
};
sankey.layout = function(iterations) {
computeNodeLinks();
computeNodeValues();
computeNodeBreadths();
computeNodeDepths(iterations);
computeLinkDepths();
return sankey;
};
sankey.relayout = function() {
computeLinkDepths();
return sankey;
};
sankey.link = function() {
var curvature = 0.5;
function link(d) {
var x0 = d.source.x + d.source.dx,
x1 = d.target.x,
xi = d3.interpolateNumber(x0, x1),
x2 = xi(curvature),
x3 = xi(1 - curvature),
y0 = d.source.y + d.sy + d.dy / 2,
y1 = d.target.y + d.ty + d.dy / 2;
return "M" + x0 + "," + y0
+ "C" + x2 + "," + y0
+ " " + x3 + "," + y1
+ " " + x1 + "," + y1;
}
link.curvature = function(_) {
if (!arguments.length) return curvature;
curvature = +_;
return link;
};
return link;
};
// Populate the sourceLinks and targetLinks for each node.
// Also, if the source and target are not objects, assume they are indices.
function computeNodeLinks() {
nodes.forEach(function(node) {
node.sourceLinks = [];
node.targetLinks = [];
});
links.forEach(function(link) {
var source = link.source,
target = link.target;
if (typeof source === "number") source = link.source = nodes[link.source];
if (typeof target === "number") target = link.target = nodes[link.target];
source.sourceLinks.push(link);
target.targetLinks.push(link);
});
}
// Compute the value (size) of each node by summing the associated links.
function computeNodeValues() {
nodes.forEach(function(node) {
node.value = Math.max(
d3.sum(node.sourceLinks, value),
d3.sum(node.targetLinks, value)
);
});
}
// Iteratively assign the breadth (x-position) for each node.
// Nodes are assigned the maximum breadth of incoming neighbors plus one;
// nodes with no incoming links are assigned breadth zero, while
// nodes with no outgoing links are assigned the maximum breadth.
function computeNodeBreadths() {
var remainingNodes = nodes,
nextNodes,
x = 0;
while (remainingNodes.length) {
nextNodes = [];
remainingNodes.forEach(function(node) {
node.x = x;
node.dx = nodeWidth;
node.sourceLinks.forEach(function(link) {
nextNodes.push(link.target);
});
});
remainingNodes = nextNodes;
++x;
}
//
moveSinksRight(x);
scaleNodeBreadths((size[0] - nodeWidth) / (x - 1));
}
function moveSourcesRight() {
nodes.forEach(function(node) {
if (!node.targetLinks.length) {
node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1;
}
});
}
function moveSinksRight(x) {
nodes.forEach(function(node) {
if (!node.sourceLinks.length) {
node.x = x - 1;
}
});
}
function scaleNodeBreadths(kx) {
nodes.forEach(function(node) {
node.x *= kx;
});
}
function computeNodeDepths(iterations) {
var nodesByBreadth = nest()
.key(function(d) { return d.x; })
.sortKeys(d3.ascending)
.entries(nodes)
.map(function(d) { return d.values; });
//
initializeNodeDepth();
resolveCollisions();
for (var alpha = 1; iterations > 0; --iterations) {
relaxRightToLeft(alpha *= .99);
resolveCollisions();
relaxLeftToRight(alpha);
resolveCollisions();
}
function initializeNodeDepth() {
var ky = d3.min(nodesByBreadth, function(nodes) {
return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value);
});
nodesByBreadth.forEach(function(nodes) {
nodes.forEach(function(node, i) {
node.y = i;
node.dy = node.value * ky;
});
});
links.forEach(function(link) {
link.dy = link.value * ky;
});
}
function relaxLeftToRight(alpha) {
nodesByBreadth.forEach(function(nodes, breadth) {
nodes.forEach(function(node) {
if (node.targetLinks.length) {
var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value);
node.y += (y - center(node)) * alpha;
}
});
});
function weightedSource(link) {
return center(link.source) * link.value;
}
}
function relaxRightToLeft(alpha) {
nodesByBreadth.slice().reverse().forEach(function(nodes) {
nodes.forEach(function(node) {
if (node.sourceLinks.length) {
var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value);
node.y += (y - center(node)) * alpha;
}
});
});
function weightedTarget(link) {
return center(link.target) * link.value;
}
}
function resolveCollisions() {
nodesByBreadth.forEach(function(nodes) {
var node,
dy,
y0 = 0,
n = nodes.length,
i;
// Push any overlapping nodes down.
nodes.sort(ascendingDepth);
for (i = 0; i < n; ++i) {
node = nodes[i];
dy = y0 - node.y;
if (dy > 0) node.y += dy;
y0 = node.y + node.dy + nodePadding;
}
// If the bottommost node goes outside the bounds, push it back up.
dy = y0 - nodePadding - size[1];
if (dy > 0) {
y0 = node.y -= dy;
// Push any overlapping nodes back up.
for (i = n - 2; i >= 0; --i) {
node = nodes[i];
dy = node.y + node.dy + nodePadding - y0;
if (dy > 0) node.y -= dy;
y0 = node.y;
}
}
});
}
function ascendingDepth(a, b) {
return a.y - b.y;
}
}
function computeLinkDepths() {
nodes.forEach(function(node) {
node.sourceLinks.sort(ascendingTargetDepth);
node.targetLinks.sort(ascendingSourceDepth);
});
nodes.forEach(function(node) {
var sy = 0, ty = 0;
node.sourceLinks.forEach(function(link) {
link.sy = sy;
sy += link.dy;
});
node.targetLinks.forEach(function(link) {
link.ty = ty;
ty += link.dy;
});
});
function ascendingSourceDepth(a, b) {
return a.source.y - b.source.y;
}
function ascendingTargetDepth(a, b) {
return a.target.y - b.target.y;
}
}
function center(node) {
return node.y + node.dy / 2;
}
function value(link) {
return link.value;
}
return sankey;
};
function nest() {
var keys = [],
sortKeys = [],
sortValues,
rollup,
nest;
function apply(array, depth, createResult, setResult) {
if (depth >= keys.length) {
if (sortValues != null) array.sort(sortValues);
return rollup != null ? rollup(array) : array;
}
var i = -1,
n = array.length,
key = keys[depth++],
keyValue,
value,
valuesByKey = map(),
values,
result = createResult();
while (++i < n) {
if (values = valuesByKey.get(keyValue = key(value = array[i]) + "")) {
values.push(value);
} else {
valuesByKey.set(keyValue, [value]);
}
}
valuesByKey.each(function(values, key) {
setResult(result, key, apply(values, depth, createResult, setResult));
});
return result;
}
function entries(map, depth) {
if (++depth > keys.length) return map;
var array, sortKey = sortKeys[depth - 1];
if (rollup != null && depth >= keys.length) array = map.entries();
else array = [], map.each(function(v, k) { array.push({key: k, values: entries(v, depth)}); });
return sortKey != null ? array.sort(function(a, b) { return sortKey(a.key, b.key); }) : array;
}
return nest = {
object: function(array) { return apply(array, 0, createObject, setObject); },
map: function(array) { return apply(array, 0, createMap, setMap); },
entries: function(array) { return entries(apply(array, 0, createMap, setMap), 0); },
key: function(d) { keys.push(d); return nest; },
sortKeys: function(order) { sortKeys[keys.length - 1] = order; return nest; },
sortValues: function(order) { sortValues = order; return nest; },
rollup: function(f) { rollup = f; return nest; }
};
}
const prefix = "$";
function Map() {}
Map.prototype = map.prototype = {
constructor: Map,
has: function(key) {
return (prefix + key) in this;
},
get: function(key) {
return this[prefix + key];
},
set: function(key, value) {
this[prefix + key] = value;
return this;
},
remove: function(key) {
var property = prefix + key;
return property in this && delete this[property];
},
clear: function() {
for (var property in this) if (property[0] === prefix) delete this[property];
},
keys: function() {
var keys = [];
for (var property in this) if (property[0] === prefix) keys.push(property.slice(1));
return keys;
},
values: function() {
var values = [];
for (var property in this) if (property[0] === prefix) values.push(this[property]);
return values;
},
entries: function() {
var entries = [];
for (var property in this) if (property[0] === prefix) entries.push({key: property.slice(1), value: this[property]});
return entries;
},
size: function() {
var size = 0;
for (var property in this) if (property[0] === prefix) ++size;
return size;
},
empty: function() {
for (var property in this) if (property[0] === prefix) return false;
return true;
},
each: function(f) {
for (var property in this) if (property[0] === prefix) f(this[property], property.slice(1), this);
}
};
function map(object, f) {
var map = new Map;
// Copy constructor.
if (object instanceof Map) object.each(function(value, key) { map.set(key, value); });
// Index array by numeric index or specified key function.
else if (Array.isArray(object)) {
var i = -1,
n = object.length,
o;
if (f == null) while (++i < n) map.set(i, object[i]);
else while (++i < n) map.set(f(o = object[i], i, object), o);
}
// Convert object to map.
else if (object) for (var key in object) map.set(key, object[key]);
return map;
}
function createObject() {
return {};
}
function setObject(object, key, value) {
object[key] = value;
}
function createMap() {
return map();
}
function setMap(map, key, value) {
map.set(key, value);
}

View File

@ -2,5 +2,10 @@ import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
plugins: [sveltekit()],
build: {
commonjsOptions: {
esmExternals: true
}
}
});