Spaces:
Running
Match table: prepend match_id column, truncate event; add stats section
Browse files- New MatchIdCell + matchIdColumn helper, prepended to all 3 column sets
(rounds / maps / matches). Match id rendered as monospace tabular-nums.
- eventColumn helper with meta.cellClass = max-w-[16rem] so long event
names truncate; EventCell wrapper also gets max-w-[16rem].
- match-table.svelte: thread column meta.cellClass / meta.headClass
through Table.Cell + Table.Head so per-column widths actually apply.
- New StatsSection between the table and BibTeX:
* Maps distribution: horizontal bars per CS2 map (count + share %),
colored using mapColorClasses for visual tie-in with badges.
* Rounds-per-map histogram: 4-round buckets. Restructure layout —
bars are direct children of h-32 flex items-end so % heights resolve
against a definite parent height; labels render in a separate row
below to avoid collapsing to zero in flex-col wrappers.
- bun.lock +101 -0
- package.json +1 -0
- src/lib/components/match-table/cells/event-cell.svelte +1 -1
- src/lib/components/match-table/cells/match-id-cell.svelte +8 -0
- src/lib/components/match-table/columns.ts +30 -24
- src/lib/components/match-table/match-table.svelte +2 -2
- src/lib/components/stats-section.svelte +118 -0
- src/lib/components/ui/chart/chart-container.svelte +80 -0
- src/lib/components/ui/chart/chart-style.svelte +37 -0
- src/lib/components/ui/chart/chart-tooltip.svelte +180 -0
- src/lib/components/ui/chart/chart-utils.ts +68 -0
- src/lib/components/ui/chart/index.ts +6 -0
- src/routes/+page.svelte +3 -0
|
@@ -23,6 +23,7 @@
|
|
| 23 |
"clsx": "^2.1.1",
|
| 24 |
"eslint": "^10.2.1",
|
| 25 |
"eslint-plugin-better-tailwindcss": "^4.5.0",
|
|
|
|
| 26 |
"mode-watcher": "^1.1.0",
|
| 27 |
"phosphor-svelte": "^3.1.0",
|
| 28 |
"prettier": "^3.8.3",
|
|
@@ -43,6 +44,10 @@
|
|
| 43 |
},
|
| 44 |
},
|
| 45 |
"packages": {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
"@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "2.8.1" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
|
| 47 |
|
| 48 |
"@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
|
|
@@ -97,6 +102,14 @@
|
|
| 97 |
|
| 98 |
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "3.1.2", "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
| 99 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "0.10.1" }, "peerDependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
|
| 101 |
|
| 102 |
"@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="],
|
|
@@ -185,6 +198,10 @@
|
|
| 185 |
|
| 186 |
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
| 187 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
"@types/dom-mediacapture-transform": ["@types/dom-mediacapture-transform@0.1.11", "", { "dependencies": { "@types/dom-webcodecs": "0.1.13" } }, "sha512-Y2p+nGf1bF2XMttBnsVPHUWzRRZzqUoJAKmiP10b5umnO6DDrWI0BrGDJy1pOHoOULVmGSfFNkQrAlC5dcj6nQ=="],
|
| 189 |
|
| 190 |
"@types/dom-webcodecs": ["@types/dom-webcodecs@0.1.13", "", {}, "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ=="],
|
|
@@ -193,6 +210,8 @@
|
|
| 193 |
|
| 194 |
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
| 195 |
|
|
|
|
|
|
|
| 196 |
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
| 197 |
|
| 198 |
"@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="],
|
|
@@ -257,12 +276,66 @@
|
|
| 257 |
|
| 258 |
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
| 259 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
| 261 |
|
| 262 |
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
| 263 |
|
| 264 |
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
| 265 |
|
|
|
|
|
|
|
| 266 |
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
| 267 |
|
| 268 |
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
|
@@ -329,12 +402,16 @@
|
|
| 329 |
|
| 330 |
"hysnappy": ["hysnappy@1.0.0", "", {}, "sha512-MNrC4NfwDGPb889O6gIfEtbvEZCSWUsSEhsz4Oq2FRcpGtXHfeVz3KciSPp5Pnnz1NjFMgDQNfxdJozymJEDDA=="],
|
| 331 |
|
|
|
|
|
|
|
| 332 |
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
| 333 |
|
| 334 |
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
| 335 |
|
| 336 |
"inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="],
|
| 337 |
|
|
|
|
|
|
|
| 338 |
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
| 339 |
|
| 340 |
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
|
@@ -357,6 +434,8 @@
|
|
| 357 |
|
| 358 |
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
| 359 |
|
|
|
|
|
|
|
| 360 |
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
| 361 |
|
| 362 |
"lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "2.1.2" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
|
|
@@ -395,6 +474,10 @@
|
|
| 395 |
|
| 396 |
"mediabunny": ["mediabunny@1.42.0", "", { "dependencies": { "@types/dom-mediacapture-transform": "0.1.11", "@types/dom-webcodecs": "0.1.13" } }, "sha512-s9ypTqLi6kbh95gC+YaJlG0PkLvMxu37Q/wO/pFZx0fUCA5Ym5mp+2dWoa83mKQ3Uo18aNlgev5iJ5ESZqWwgQ=="],
|
| 397 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 398 |
"minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
| 399 |
|
| 400 |
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
|
@@ -447,12 +530,18 @@
|
|
| 447 |
|
| 448 |
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
| 449 |
|
|
|
|
|
|
|
| 450 |
"rolldown": ["rolldown@1.0.0-rc.17", "", { "dependencies": { "@oxc-project/types": "0.127.0", "@rolldown/pluginutils": "1.0.0-rc.17" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-x64": "1.0.0-rc.17", "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA=="],
|
| 451 |
|
| 452 |
"runed": ["runed@0.35.1", "", { "dependencies": { "dequal": "2.0.3", "esm-env": "1.2.2", "lz-string": "1.5.0" }, "optionalDependencies": { "@sveltejs/kit": "2.58.0" }, "peerDependencies": { "svelte": "5.55.5" } }, "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q=="],
|
| 453 |
|
|
|
|
|
|
|
| 454 |
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "1.2.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
|
| 455 |
|
|
|
|
|
|
|
| 456 |
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
| 457 |
|
| 458 |
"set-cookie-parser": ["set-cookie-parser@3.1.0", "", {}, "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw=="],
|
|
@@ -541,6 +630,14 @@
|
|
| 541 |
|
| 542 |
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
| 543 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 544 |
"mode-watcher/runed": ["runed@0.25.0", "", { "dependencies": { "esm-env": "1.2.2" }, "peerDependencies": { "svelte": "5.55.5" } }, "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg=="],
|
| 545 |
|
| 546 |
"mode-watcher/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "2.1.1", "runed": "0.23.4", "style-to-object": "1.0.14" }, "peerDependencies": { "svelte": "5.55.5" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="],
|
|
@@ -553,6 +650,10 @@
|
|
| 553 |
|
| 554 |
"svelte-sonner/runed": ["runed@0.28.0", "", { "dependencies": { "esm-env": "1.2.2" }, "peerDependencies": { "svelte": "5.55.5" } }, "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ=="],
|
| 555 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 556 |
"mode-watcher/svelte-toolbelt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "1.2.2" }, "peerDependencies": { "svelte": "5.55.5" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="],
|
| 557 |
}
|
| 558 |
}
|
|
|
|
| 23 |
"clsx": "^2.1.1",
|
| 24 |
"eslint": "^10.2.1",
|
| 25 |
"eslint-plugin-better-tailwindcss": "^4.5.0",
|
| 26 |
+
"layerchart": "2.0.0-next.48",
|
| 27 |
"mode-watcher": "^1.1.0",
|
| 28 |
"phosphor-svelte": "^3.1.0",
|
| 29 |
"prettier": "^3.8.3",
|
|
|
|
| 44 |
},
|
| 45 |
},
|
| 46 |
"packages": {
|
| 47 |
+
"@dagrejs/dagre": ["@dagrejs/dagre@2.0.4", "", { "dependencies": { "@dagrejs/graphlib": "3.0.4" } }, "sha512-J6vCWTNpicHF4zFlZG1cS5DkGzMr9941gddYkakjrg3ZNev4bbqEgLHFTWiFrcJm7UCRu7olO3K6IRDd9gSGhA=="],
|
| 48 |
+
|
| 49 |
+
"@dagrejs/graphlib": ["@dagrejs/graphlib@3.0.4", "", {}, "sha512-HxZ7fCvAwTLCWCO0WjDkzAFQze8LdC6iOpKbetDKHIuDfIgMlIzYzqZ4nxwLlclQX+3ZVeZ1K2OuaOE2WWcyOg=="],
|
| 50 |
+
|
| 51 |
"@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "2.8.1" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
|
| 52 |
|
| 53 |
"@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
|
|
|
|
| 102 |
|
| 103 |
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "3.1.2", "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
| 104 |
|
| 105 |
+
"@layerstack/svelte-actions": ["@layerstack/svelte-actions@1.0.1-next.18", "", { "dependencies": { "@floating-ui/dom": "^1.7.0", "@layerstack/utils": "2.0.0-next.18", "d3-scale": "^4.0.2" } }, "sha512-gxPzCnJ1c9LTfWtRqLUzefCx+k59ZpxDUQ2XB+LokveZQPe7IDSOwHaBOEMlaGoGrtwc3Ft8dSZq+2WT2o9u/g=="],
|
| 106 |
+
|
| 107 |
+
"@layerstack/svelte-state": ["@layerstack/svelte-state@0.1.0-next.23", "", { "dependencies": { "@layerstack/utils": "2.0.0-next.18" } }, "sha512-7O4umv+gXwFfs3/vjzFWYHNXGwYnnjBapWJ5Y+9u99F4eVk6rh4ocNwqkqQNkpMZ5tUJBlRTWjPE1So6+hEzIg=="],
|
| 108 |
+
|
| 109 |
+
"@layerstack/tailwind": ["@layerstack/tailwind@2.0.0-next.21", "", { "dependencies": { "@layerstack/utils": "^2.0.0-next.18", "clsx": "^2.1.1", "d3-array": "^3.2.4", "tailwind-merge": "^3.2.0" } }, "sha512-Qgp2EpmEHmjtura8MQzWicR6ztBRSsRvddakFtx9ShrLMz6jWzd6bCMVVRu44Q3ZOrtXmSu4QxjCZWu1ytvuPg=="],
|
| 110 |
+
|
| 111 |
+
"@layerstack/utils": ["@layerstack/utils@2.0.0-next.18", "", { "dependencies": { "d3-array": "^3.2.4", "d3-time": "^3.1.0", "d3-time-format": "^4.1.0" } }, "sha512-EYILHpfBRYMMEahajInu9C2AXQom5IcAEdtCeucD3QIl/fdDgRbtzn6/8QW9ewumfyNZetdUvitOksmI1+gZYQ=="],
|
| 112 |
+
|
| 113 |
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "0.10.1" }, "peerDependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
|
| 114 |
|
| 115 |
"@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="],
|
|
|
|
| 198 |
|
| 199 |
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
| 200 |
|
| 201 |
+
"@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
|
| 202 |
+
|
| 203 |
+
"@types/d3-contour": ["@types/d3-contour@3.0.6", "", { "dependencies": { "@types/d3-array": "*", "@types/geojson": "*" } }, "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg=="],
|
| 204 |
+
|
| 205 |
"@types/dom-mediacapture-transform": ["@types/dom-mediacapture-transform@0.1.11", "", { "dependencies": { "@types/dom-webcodecs": "0.1.13" } }, "sha512-Y2p+nGf1bF2XMttBnsVPHUWzRRZzqUoJAKmiP10b5umnO6DDrWI0BrGDJy1pOHoOULVmGSfFNkQrAlC5dcj6nQ=="],
|
| 206 |
|
| 207 |
"@types/dom-webcodecs": ["@types/dom-webcodecs@0.1.13", "", {}, "sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ=="],
|
|
|
|
| 210 |
|
| 211 |
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
| 212 |
|
| 213 |
+
"@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
|
| 214 |
+
|
| 215 |
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
| 216 |
|
| 217 |
"@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="],
|
|
|
|
| 276 |
|
| 277 |
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
|
| 278 |
|
| 279 |
+
"d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
|
| 280 |
+
|
| 281 |
+
"d3-chord": ["d3-chord@3.0.1", "", { "dependencies": { "d3-path": "1 - 3" } }, "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g=="],
|
| 282 |
+
|
| 283 |
+
"d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
|
| 284 |
+
|
| 285 |
+
"d3-contour": ["d3-contour@4.0.2", "", { "dependencies": { "d3-array": "^3.2.0" } }, "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA=="],
|
| 286 |
+
|
| 287 |
+
"d3-delaunay": ["d3-delaunay@6.0.4", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="],
|
| 288 |
+
|
| 289 |
+
"d3-dispatch": ["d3-dispatch@3.0.1", "", {}, "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg=="],
|
| 290 |
+
|
| 291 |
+
"d3-dsv": ["d3-dsv@3.0.1", "", { "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" } }, "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q=="],
|
| 292 |
+
|
| 293 |
+
"d3-force": ["d3-force@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-quadtree": "1 - 3", "d3-timer": "1 - 3" } }, "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg=="],
|
| 294 |
+
|
| 295 |
+
"d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="],
|
| 296 |
+
|
| 297 |
+
"d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="],
|
| 298 |
+
|
| 299 |
+
"d3-geo-voronoi": ["d3-geo-voronoi@2.1.0", "", { "dependencies": { "d3-array": "3", "d3-delaunay": "6", "d3-geo": "3", "d3-tricontour": "1" } }, "sha512-kqE4yYuOjPbKdBXG0xztCacPwkVSK2REF1opSNrnqqtXJmNcM++UbwQ8SxvwP6IQTj9RvIjjK4qeiVsEfj0Z2Q=="],
|
| 300 |
+
|
| 301 |
+
"d3-hierarchy": ["d3-hierarchy@3.1.2", "", {}, "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA=="],
|
| 302 |
+
|
| 303 |
+
"d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
|
| 304 |
+
|
| 305 |
+
"d3-interpolate-path": ["d3-interpolate-path@2.3.0", "", {}, "sha512-tZYtGXxBmbgHsIc9Wms6LS5u4w6KbP8C09a4/ZYc4KLMYYqub57rRBUgpUr2CIarIrJEpdAWWxWQvofgaMpbKQ=="],
|
| 306 |
+
|
| 307 |
+
"d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
|
| 308 |
+
|
| 309 |
+
"d3-quadtree": ["d3-quadtree@3.0.1", "", {}, "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw=="],
|
| 310 |
+
|
| 311 |
+
"d3-random": ["d3-random@3.0.1", "", {}, "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ=="],
|
| 312 |
+
|
| 313 |
+
"d3-sankey": ["d3-sankey@0.12.3", "", { "dependencies": { "d3-array": "1 - 2", "d3-shape": "^1.2.0" } }, "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ=="],
|
| 314 |
+
|
| 315 |
+
"d3-scale": ["d3-scale@4.0.2", "", { "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" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
|
| 316 |
+
|
| 317 |
+
"d3-scale-chromatic": ["d3-scale-chromatic@3.1.0", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="],
|
| 318 |
+
|
| 319 |
+
"d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
|
| 320 |
+
|
| 321 |
+
"d3-tile": ["d3-tile@1.0.0", "", {}, "sha512-79fnTKpPMPDS5xQ0xuS9ir0165NEwwkFpe/DSOmc2Gl9ldYzKKRDWogmTTE8wAJ8NA7PMapNfEcyKhI9Lxdu5Q=="],
|
| 322 |
+
|
| 323 |
+
"d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
|
| 324 |
+
|
| 325 |
+
"d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="],
|
| 326 |
+
|
| 327 |
+
"d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
|
| 328 |
+
|
| 329 |
+
"d3-tricontour": ["d3-tricontour@1.1.0", "", { "dependencies": { "d3-delaunay": "6", "d3-scale": "4" } }, "sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ=="],
|
| 330 |
+
|
| 331 |
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
| 332 |
|
| 333 |
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
|
| 334 |
|
| 335 |
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
| 336 |
|
| 337 |
+
"delaunator": ["delaunator@5.1.0", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ=="],
|
| 338 |
+
|
| 339 |
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
| 340 |
|
| 341 |
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
|
|
|
| 402 |
|
| 403 |
"hysnappy": ["hysnappy@1.0.0", "", {}, "sha512-MNrC4NfwDGPb889O6gIfEtbvEZCSWUsSEhsz4Oq2FRcpGtXHfeVz3KciSPp5Pnnz1NjFMgDQNfxdJozymJEDDA=="],
|
| 404 |
|
| 405 |
+
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
| 406 |
+
|
| 407 |
"ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
|
| 408 |
|
| 409 |
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
|
| 410 |
|
| 411 |
"inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="],
|
| 412 |
|
| 413 |
+
"internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
|
| 414 |
+
|
| 415 |
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
| 416 |
|
| 417 |
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
|
|
|
| 434 |
|
| 435 |
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
| 436 |
|
| 437 |
+
"layerchart": ["layerchart@2.0.0-next.48", "", { "dependencies": { "@dagrejs/dagre": "^2.0.4", "@layerstack/svelte-actions": "1.0.1-next.18", "@layerstack/svelte-state": "0.1.0-next.23", "@layerstack/tailwind": "2.0.0-next.21", "@layerstack/utils": "2.0.0-next.18", "@types/d3-contour": "^3.0.6", "d3-array": "^3.2.4", "d3-chord": "^3.0.1", "d3-color": "^3.1.0", "d3-contour": "^4.0.2", "d3-delaunay": "^6.0.4", "d3-dsv": "^3.0.1", "d3-force": "^3.0.0", "d3-geo": "^3.1.1", "d3-geo-voronoi": "^2.1.0", "d3-hierarchy": "^3.1.2", "d3-interpolate": "^3.0.1", "d3-interpolate-path": "^2.3.0", "d3-path": "^3.1.0", "d3-quadtree": "^3.0.1", "d3-random": "^3.0.1", "d3-sankey": "^0.12.3", "d3-scale": "^4.0.2", "d3-scale-chromatic": "^3.1.0", "d3-shape": "^3.2.0", "d3-tile": "^1.0.0", "d3-time": "^3.1.0", "memoize": "^10.2.0", "runed": "^0.37.1" }, "peerDependencies": { "svelte": "^5.0.0" } }, "sha512-XoEYBztamA8lMxtF/Jz3aDX0HMk8dI+o4fK9fSl8ecT2Tdx3DQUjtKGtlQAOFdwC/AWifeLmKq5cMTQt9COZPQ=="],
|
| 438 |
+
|
| 439 |
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
|
| 440 |
|
| 441 |
"lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "2.1.2" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
|
|
|
|
| 474 |
|
| 475 |
"mediabunny": ["mediabunny@1.42.0", "", { "dependencies": { "@types/dom-mediacapture-transform": "0.1.11", "@types/dom-webcodecs": "0.1.13" } }, "sha512-s9ypTqLi6kbh95gC+YaJlG0PkLvMxu37Q/wO/pFZx0fUCA5Ym5mp+2dWoa83mKQ3Uo18aNlgev5iJ5ESZqWwgQ=="],
|
| 476 |
|
| 477 |
+
"memoize": ["memoize@10.2.0", "", { "dependencies": { "mimic-function": "^5.0.1" } }, "sha512-DeC6b7QBrZsRs3Y02A6A7lQyzFbsQbqgjI6UW0GigGWV+u1s25TycMr0XHZE4cJce7rY/vyw2ctMQqfDkIhUEA=="],
|
| 478 |
+
|
| 479 |
+
"mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
|
| 480 |
+
|
| 481 |
"minimatch": ["minimatch@10.2.5", "", { "dependencies": { "brace-expansion": "^5.0.5" } }, "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg=="],
|
| 482 |
|
| 483 |
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
|
|
|
| 530 |
|
| 531 |
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
| 532 |
|
| 533 |
+
"robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="],
|
| 534 |
+
|
| 535 |
"rolldown": ["rolldown@1.0.0-rc.17", "", { "dependencies": { "@oxc-project/types": "0.127.0", "@rolldown/pluginutils": "1.0.0-rc.17" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-x64": "1.0.0-rc.17", "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA=="],
|
| 536 |
|
| 537 |
"runed": ["runed@0.35.1", "", { "dependencies": { "dequal": "2.0.3", "esm-env": "1.2.2", "lz-string": "1.5.0" }, "optionalDependencies": { "@sveltejs/kit": "2.58.0" }, "peerDependencies": { "svelte": "5.55.5" } }, "sha512-2F4Q/FZzbeJTFdIS/PuOoPRSm92sA2LhzTnv6FXhCoENb3huf5+fDuNOg1LNvGOouy3u/225qxmuJvcV3IZK5Q=="],
|
| 538 |
|
| 539 |
+
"rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
|
| 540 |
+
|
| 541 |
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "1.2.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
|
| 542 |
|
| 543 |
+
"safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
|
| 544 |
+
|
| 545 |
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
|
| 546 |
|
| 547 |
"set-cookie-parser": ["set-cookie-parser@3.1.0", "", {}, "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw=="],
|
|
|
|
| 630 |
|
| 631 |
"@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
| 632 |
|
| 633 |
+
"d3-dsv/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="],
|
| 634 |
+
|
| 635 |
+
"d3-sankey/d3-array": ["d3-array@2.12.1", "", { "dependencies": { "internmap": "^1.0.0" } }, "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ=="],
|
| 636 |
+
|
| 637 |
+
"d3-sankey/d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="],
|
| 638 |
+
|
| 639 |
+
"layerchart/runed": ["runed@0.37.1", "", { "dependencies": { "dequal": "^2.0.3", "esm-env": "^1.0.0", "lz-string": "^1.5.0" }, "peerDependencies": { "@sveltejs/kit": "^2.21.0", "svelte": "^5.7.0", "zod": "^4.1.0" }, "optionalPeers": ["@sveltejs/kit", "zod"] }, "sha512-MeFY73xBW8IueWBm012nNFIGy19WUGPLtknavyUPMpnyt350M47PhGSGrGoSLbidwn+Zlt/O0cp8/OZE3LASWA=="],
|
| 640 |
+
|
| 641 |
"mode-watcher/runed": ["runed@0.25.0", "", { "dependencies": { "esm-env": "1.2.2" }, "peerDependencies": { "svelte": "5.55.5" } }, "sha512-7+ma4AG9FT2sWQEA0Egf6mb7PBT2vHyuHail1ie8ropfSjvZGtEAx8YTmUjv/APCsdRRxEVvArNjALk9zFSOrg=="],
|
| 642 |
|
| 643 |
"mode-watcher/svelte-toolbelt": ["svelte-toolbelt@0.7.1", "", { "dependencies": { "clsx": "2.1.1", "runed": "0.23.4", "style-to-object": "1.0.14" }, "peerDependencies": { "svelte": "5.55.5" } }, "sha512-HcBOcR17Vx9bjaOceUvxkY3nGmbBmCBBbuWLLEWO6jtmWH8f/QoWmbyUfQZrpDINH39en1b8mptfPQT9VKQ1xQ=="],
|
|
|
|
| 650 |
|
| 651 |
"svelte-sonner/runed": ["runed@0.28.0", "", { "dependencies": { "esm-env": "1.2.2" }, "peerDependencies": { "svelte": "5.55.5" } }, "sha512-k2xx7RuO9hWcdd9f+8JoBeqWtYrm5CALfgpkg2YDB80ds/QE4w0qqu34A7fqiAwiBBSBQOid7TLxwxVC27ymWQ=="],
|
| 652 |
|
| 653 |
+
"d3-sankey/d3-array/internmap": ["internmap@1.0.1", "", {}, "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="],
|
| 654 |
+
|
| 655 |
+
"d3-sankey/d3-shape/d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="],
|
| 656 |
+
|
| 657 |
"mode-watcher/svelte-toolbelt/runed": ["runed@0.23.4", "", { "dependencies": { "esm-env": "1.2.2" }, "peerDependencies": { "svelte": "5.55.5" } }, "sha512-9q8oUiBYeXIDLWNK5DfCWlkL0EW3oGbk845VdKlPeia28l751VpfesaB/+7pI6rnbx1I6rqoZ2fZxptOJLxILA=="],
|
| 658 |
}
|
| 659 |
}
|
|
@@ -32,6 +32,7 @@
|
|
| 32 |
"clsx": "^2.1.1",
|
| 33 |
"eslint": "^10.2.1",
|
| 34 |
"eslint-plugin-better-tailwindcss": "^4.5.0",
|
|
|
|
| 35 |
"mode-watcher": "^1.1.0",
|
| 36 |
"phosphor-svelte": "^3.1.0",
|
| 37 |
"prettier": "^3.8.3",
|
|
|
|
| 32 |
"clsx": "^2.1.1",
|
| 33 |
"eslint": "^10.2.1",
|
| 34 |
"eslint-plugin-better-tailwindcss": "^4.5.0",
|
| 35 |
+
"layerchart": "2.0.0-next.48",
|
| 36 |
"mode-watcher": "^1.1.0",
|
| 37 |
"phosphor-svelte": "^3.1.0",
|
| 38 |
"prettier": "^3.8.3",
|
|
@@ -6,7 +6,7 @@
|
|
| 6 |
let { event, format }: Props = $props();
|
| 7 |
</script>
|
| 8 |
|
| 9 |
-
<div class="flex min-w-0 flex-col">
|
| 10 |
<span class="truncate text-sm font-medium" title={event}>{event}</span>
|
| 11 |
{#if format}
|
| 12 |
<span class="text-[0.65rem] tracking-wide text-muted-foreground/70 uppercase">{format}</span>
|
|
|
|
| 6 |
let { event, format }: Props = $props();
|
| 7 |
</script>
|
| 8 |
|
| 9 |
+
<div class="flex max-w-[16rem] min-w-0 flex-col">
|
| 10 |
<span class="truncate text-sm font-medium" title={event}>{event}</span>
|
| 11 |
{#if format}
|
| 12 |
<span class="text-[0.65rem] tracking-wide text-muted-foreground/70 uppercase">{format}</span>
|
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
interface Props {
|
| 3 |
+
matchId: number;
|
| 4 |
+
}
|
| 5 |
+
let { matchId }: Props = $props();
|
| 6 |
+
</script>
|
| 7 |
+
|
| 8 |
+
<span class="font-mono text-xs text-muted-foreground/80 tabular-nums">{matchId}</span>
|
|
@@ -3,6 +3,7 @@ import { renderComponent } from '$lib/components/ui/data-table';
|
|
| 3 |
import SortHeader from './data-table-sort-header.svelte';
|
| 4 |
import PlainHeader from './data-table-plain-header.svelte';
|
| 5 |
import EventCell from './cells/event-cell.svelte';
|
|
|
|
| 6 |
import TeamsCell from './cells/teams-cell.svelte';
|
| 7 |
import ScoreCell from './cells/score-cell.svelte';
|
| 8 |
import MapCell from './cells/map-cell.svelte';
|
|
@@ -18,6 +19,8 @@ import type { Match } from '$lib/types';
|
|
| 18 |
declare module '@tanstack/table-core' {
|
| 19 |
interface ColumnMeta<TData extends RowData, TValue> {
|
| 20 |
label?: string;
|
|
|
|
|
|
|
| 21 |
}
|
| 22 |
}
|
| 23 |
|
|
@@ -27,15 +30,30 @@ const dateSort = (a: { match_date: string }, b: { match_date: string }) =>
|
|
| 27 |
// Cells use `accessorFn` returning the underlying value so global filtering and
|
| 28 |
// sorting see the right thing; visual rendering happens in `cell` via components.
|
| 29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
export const roundColumns: ColumnDef<RoundRow>[] = [
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
accessorKey: 'event',
|
| 34 |
-
header: ({ column }) => renderComponent(SortHeader, { column, label: 'Event' }),
|
| 35 |
-
cell: ({ row }) =>
|
| 36 |
-
renderComponent(EventCell, { event: row.original.event, format: row.original.format }),
|
| 37 |
-
meta: { label: 'Event' }
|
| 38 |
-
},
|
| 39 |
{
|
| 40 |
id: 'map_name',
|
| 41 |
accessorKey: 'map_name',
|
|
@@ -90,14 +108,8 @@ export const roundColumns: ColumnDef<RoundRow>[] = [
|
|
| 90 |
];
|
| 91 |
|
| 92 |
export const mapColumns: ColumnDef<MapRow>[] = [
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
accessorKey: 'event',
|
| 96 |
-
header: ({ column }) => renderComponent(SortHeader, { column, label: 'Event' }),
|
| 97 |
-
cell: ({ row }) =>
|
| 98 |
-
renderComponent(EventCell, { event: row.original.event, format: row.original.format }),
|
| 99 |
-
meta: { label: 'Event' }
|
| 100 |
-
},
|
| 101 |
{
|
| 102 |
id: 'map_name',
|
| 103 |
accessorKey: 'map_name',
|
|
@@ -177,14 +189,8 @@ export const mapColumns: ColumnDef<MapRow>[] = [
|
|
| 177 |
];
|
| 178 |
|
| 179 |
export const matchColumns: ColumnDef<MatchRow>[] = [
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
accessorKey: 'event',
|
| 183 |
-
header: ({ column }) => renderComponent(SortHeader, { column, label: 'Event' }),
|
| 184 |
-
cell: ({ row }) =>
|
| 185 |
-
renderComponent(EventCell, { event: row.original.event, format: row.original.format }),
|
| 186 |
-
meta: { label: 'Event' }
|
| 187 |
-
},
|
| 188 |
{
|
| 189 |
id: 'teams',
|
| 190 |
accessorFn: (r) => `${r.team1} ${r.team2}`,
|
|
|
|
| 3 |
import SortHeader from './data-table-sort-header.svelte';
|
| 4 |
import PlainHeader from './data-table-plain-header.svelte';
|
| 5 |
import EventCell from './cells/event-cell.svelte';
|
| 6 |
+
import MatchIdCell from './cells/match-id-cell.svelte';
|
| 7 |
import TeamsCell from './cells/teams-cell.svelte';
|
| 8 |
import ScoreCell from './cells/score-cell.svelte';
|
| 9 |
import MapCell from './cells/map-cell.svelte';
|
|
|
|
| 19 |
declare module '@tanstack/table-core' {
|
| 20 |
interface ColumnMeta<TData extends RowData, TValue> {
|
| 21 |
label?: string;
|
| 22 |
+
cellClass?: string;
|
| 23 |
+
headClass?: string;
|
| 24 |
}
|
| 25 |
}
|
| 26 |
|
|
|
|
| 30 |
// Cells use `accessorFn` returning the underlying value so global filtering and
|
| 31 |
// sorting see the right thing; visual rendering happens in `cell` via components.
|
| 32 |
|
| 33 |
+
const matchIdColumn = <T extends { match_id: number }>(): ColumnDef<T> => ({
|
| 34 |
+
id: 'match_id',
|
| 35 |
+
accessorKey: 'match_id',
|
| 36 |
+
header: ({ column }) => renderComponent(SortHeader, { column, label: 'Match' }),
|
| 37 |
+
cell: ({ row }) => renderComponent(MatchIdCell, { matchId: row.original.match_id }),
|
| 38 |
+
meta: { label: 'Match', cellClass: 'w-[6rem] pr-2', headClass: 'w-[6rem] pr-2' }
|
| 39 |
+
});
|
| 40 |
+
|
| 41 |
+
const eventColumn = <T extends { event: string; format?: string }>(): ColumnDef<T> => ({
|
| 42 |
+
id: 'event',
|
| 43 |
+
accessorKey: 'event',
|
| 44 |
+
header: ({ column }) => renderComponent(SortHeader, { column, label: 'Event' }),
|
| 45 |
+
cell: ({ row }) =>
|
| 46 |
+
renderComponent(EventCell, { event: row.original.event, format: row.original.format }),
|
| 47 |
+
meta: {
|
| 48 |
+
label: 'Event',
|
| 49 |
+
cellClass: 'max-w-[16rem]',
|
| 50 |
+
headClass: 'max-w-[16rem]'
|
| 51 |
+
}
|
| 52 |
+
});
|
| 53 |
+
|
| 54 |
export const roundColumns: ColumnDef<RoundRow>[] = [
|
| 55 |
+
matchIdColumn<RoundRow>(),
|
| 56 |
+
eventColumn<RoundRow>(),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
{
|
| 58 |
id: 'map_name',
|
| 59 |
accessorKey: 'map_name',
|
|
|
|
| 108 |
];
|
| 109 |
|
| 110 |
export const mapColumns: ColumnDef<MapRow>[] = [
|
| 111 |
+
matchIdColumn<MapRow>(),
|
| 112 |
+
eventColumn<MapRow>(),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
{
|
| 114 |
id: 'map_name',
|
| 115 |
accessorKey: 'map_name',
|
|
|
|
| 189 |
];
|
| 190 |
|
| 191 |
export const matchColumns: ColumnDef<MatchRow>[] = [
|
| 192 |
+
matchIdColumn<MatchRow>(),
|
| 193 |
+
eventColumn<MatchRow>(),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
{
|
| 195 |
id: 'teams',
|
| 196 |
accessorFn: (r) => `${r.team1} ${r.team2}`,
|
|
@@ -126,7 +126,7 @@
|
|
| 126 |
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
| 127 |
<Table.Row class="bg-muted/40 hover:bg-muted/40">
|
| 128 |
{#each headerGroup.headers as header (header.id)}
|
| 129 |
-
<Table.Head class=
|
| 130 |
{#if !header.isPlaceholder}
|
| 131 |
<FlexRender
|
| 132 |
content={header.column.columnDef.header}
|
|
@@ -153,7 +153,7 @@
|
|
| 153 |
role={getRowHref ? 'link' : undefined}
|
| 154 |
>
|
| 155 |
{#each row.getVisibleCells() as cell (cell.id)}
|
| 156 |
-
<Table.Cell class=
|
| 157 |
<FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} />
|
| 158 |
</Table.Cell>
|
| 159 |
{/each}
|
|
|
|
| 126 |
{#each table.getHeaderGroups() as headerGroup (headerGroup.id)}
|
| 127 |
<Table.Row class="bg-muted/40 hover:bg-muted/40">
|
| 128 |
{#each headerGroup.headers as header (header.id)}
|
| 129 |
+
<Table.Head class={cn('h-9 text-xs', header.column.columnDef.meta?.headClass)}>
|
| 130 |
{#if !header.isPlaceholder}
|
| 131 |
<FlexRender
|
| 132 |
content={header.column.columnDef.header}
|
|
|
|
| 153 |
role={getRowHref ? 'link' : undefined}
|
| 154 |
>
|
| 155 |
{#each row.getVisibleCells() as cell (cell.id)}
|
| 156 |
+
<Table.Cell class={cn('py-2 align-middle', cell.column.columnDef.meta?.cellClass)}>
|
| 157 |
<FlexRender content={cell.column.columnDef.cell} context={cell.getContext()} />
|
| 158 |
</Table.Cell>
|
| 159 |
{/each}
|
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
import type { Match } from '$lib/types';
|
| 3 |
+
import { prettyMap } from '$lib/utils/format';
|
| 4 |
+
import { mapColorClasses } from '$lib/utils/map-colors';
|
| 5 |
+
import { cn } from '$lib/utils';
|
| 6 |
+
|
| 7 |
+
interface Props {
|
| 8 |
+
matches: Match[];
|
| 9 |
+
}
|
| 10 |
+
let { matches }: Props = $props();
|
| 11 |
+
|
| 12 |
+
const mapsDist = $derived.by(() => {
|
| 13 |
+
const counts = new Map<string, number>();
|
| 14 |
+
for (const m of matches) counts.set(m.map_name, (counts.get(m.map_name) ?? 0) + 1);
|
| 15 |
+
return Array.from(counts.entries())
|
| 16 |
+
.map(([name, count]) => ({ name, count }))
|
| 17 |
+
.sort((a, b) => b.count - a.count);
|
| 18 |
+
});
|
| 19 |
+
|
| 20 |
+
const totalMaps = $derived(mapsDist.reduce((s, m) => s + m.count, 0));
|
| 21 |
+
const maxCount = $derived(Math.max(1, ...mapsDist.map((m) => m.count)));
|
| 22 |
+
|
| 23 |
+
// Histogram of rounds-per-match — buckets of 4 rounds.
|
| 24 |
+
const roundsHist = $derived.by(() => {
|
| 25 |
+
const buckets = new Map<string, number>();
|
| 26 |
+
for (const m of matches) {
|
| 27 |
+
const r = m.rounds_played;
|
| 28 |
+
if (!r) continue;
|
| 29 |
+
// pinch into [start, start+3] buckets; e.g. 13-16, 17-20, etc.
|
| 30 |
+
const start = Math.floor((r - 1) / 4) * 4 + 1;
|
| 31 |
+
const key = `${start}-${start + 3}`;
|
| 32 |
+
buckets.set(key, (buckets.get(key) ?? 0) + 1);
|
| 33 |
+
}
|
| 34 |
+
return Array.from(buckets.entries())
|
| 35 |
+
.map(([range, count]) => ({ range, count, sortKey: Number(range.split('-')[0]) }))
|
| 36 |
+
.sort((a, b) => a.sortKey - b.sortKey);
|
| 37 |
+
});
|
| 38 |
+
const roundsMax = $derived(Math.max(1, ...roundsHist.map((b) => b.count)));
|
| 39 |
+
</script>
|
| 40 |
+
|
| 41 |
+
<section class="mx-auto mt-12 max-w-5xl">
|
| 42 |
+
<h2
|
| 43 |
+
class="text-center font-heading text-xs font-semibold tracking-[0.2em] text-muted-foreground uppercase"
|
| 44 |
+
>
|
| 45 |
+
Statistics
|
| 46 |
+
</h2>
|
| 47 |
+
|
| 48 |
+
<div class="mt-6 grid gap-4 lg:grid-cols-2">
|
| 49 |
+
<!-- Maps distribution -->
|
| 50 |
+
<div class="rounded-md border bg-card p-4">
|
| 51 |
+
<div class="mb-3 flex items-baseline justify-between gap-2">
|
| 52 |
+
<div>
|
| 53 |
+
<div class="font-heading text-sm font-semibold">Maps distribution</div>
|
| 54 |
+
<p class="text-[11px] text-muted-foreground">
|
| 55 |
+
Maps rendered per CS2 map ({totalMaps} total)
|
| 56 |
+
</p>
|
| 57 |
+
</div>
|
| 58 |
+
</div>
|
| 59 |
+
<div class="space-y-1">
|
| 60 |
+
{#each mapsDist as m (m.name)}
|
| 61 |
+
{@const pct = (m.count / maxCount) * 100}
|
| 62 |
+
{@const sharePct = totalMaps ? (m.count / totalMaps) * 100 : 0}
|
| 63 |
+
<div class="grid grid-cols-[5.5rem_1fr_3.5rem] items-center gap-2 text-xs">
|
| 64 |
+
<span
|
| 65 |
+
class={cn(
|
| 66 |
+
'truncate rounded-sm border px-1.5 py-0.5 text-center text-[10px] font-medium capitalize',
|
| 67 |
+
mapColorClasses(m.name)
|
| 68 |
+
)}
|
| 69 |
+
title={m.name}>{prettyMap(m.name)}</span
|
| 70 |
+
>
|
| 71 |
+
<div class="relative h-3 overflow-hidden rounded-sm bg-muted/40">
|
| 72 |
+
<div
|
| 73 |
+
class={cn('absolute inset-y-0 left-0 rounded-sm border-r', mapColorClasses(m.name))}
|
| 74 |
+
style="width: {pct}%"
|
| 75 |
+
></div>
|
| 76 |
+
</div>
|
| 77 |
+
<span class="text-right font-mono text-muted-foreground tabular-nums">
|
| 78 |
+
{m.count}<span class="ml-1 text-[10px] text-muted-foreground/60"
|
| 79 |
+
>{sharePct.toFixed(0)}%</span
|
| 80 |
+
>
|
| 81 |
+
</span>
|
| 82 |
+
</div>
|
| 83 |
+
{/each}
|
| 84 |
+
</div>
|
| 85 |
+
</div>
|
| 86 |
+
|
| 87 |
+
<!-- Rounds-per-match histogram -->
|
| 88 |
+
<div class="rounded-md border bg-card p-4">
|
| 89 |
+
<div class="mb-3">
|
| 90 |
+
<div class="font-heading text-sm font-semibold">Rounds per map</div>
|
| 91 |
+
<p class="text-[11px] text-muted-foreground">
|
| 92 |
+
How many maps fall into each round-count band (regulation = 24)
|
| 93 |
+
</p>
|
| 94 |
+
</div>
|
| 95 |
+
<!-- Two rows: bars (definite-height parent so the % heights resolve) + labels. -->
|
| 96 |
+
<div class="flex h-32 items-end gap-1.5">
|
| 97 |
+
{#each roundsHist as b (b.range)}
|
| 98 |
+
{@const h = Math.max(2, (b.count / roundsMax) * 100)}
|
| 99 |
+
<div
|
| 100 |
+
class="min-w-0 flex-1 rounded-sm bg-foreground/70 transition-colors hover:bg-foreground"
|
| 101 |
+
style="height: {h}%"
|
| 102 |
+
title={`${b.range} rounds: ${b.count} maps`}
|
| 103 |
+
></div>
|
| 104 |
+
{/each}
|
| 105 |
+
</div>
|
| 106 |
+
<div class="mt-1 flex gap-1.5">
|
| 107 |
+
{#each roundsHist as b (b.range)}
|
| 108 |
+
<div
|
| 109 |
+
class="min-w-0 flex-1 truncate text-center font-mono text-[10px] text-muted-foreground/80"
|
| 110 |
+
>
|
| 111 |
+
{b.range}
|
| 112 |
+
</div>
|
| 113 |
+
{/each}
|
| 114 |
+
</div>
|
| 115 |
+
<div class="mt-1 text-center text-[10px] text-muted-foreground/70">rounds played</div>
|
| 116 |
+
</div>
|
| 117 |
+
</div>
|
| 118 |
+
</section>
|
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
import { cn, type WithElementRef } from '$lib/utils.js';
|
| 3 |
+
import type { HTMLAttributes } from 'svelte/elements';
|
| 4 |
+
import ChartStyle from './chart-style.svelte';
|
| 5 |
+
import { setChartContext, type ChartConfig } from './chart-utils.js';
|
| 6 |
+
|
| 7 |
+
const uid = $props.id();
|
| 8 |
+
|
| 9 |
+
let {
|
| 10 |
+
ref = $bindable(null),
|
| 11 |
+
id = uid,
|
| 12 |
+
class: className,
|
| 13 |
+
children,
|
| 14 |
+
config,
|
| 15 |
+
...restProps
|
| 16 |
+
}: WithElementRef<HTMLAttributes<HTMLElement>> & {
|
| 17 |
+
config: ChartConfig;
|
| 18 |
+
} = $props();
|
| 19 |
+
|
| 20 |
+
const chartId = $derived(`chart-${id || uid.replace(/:/g, '')}`);
|
| 21 |
+
|
| 22 |
+
setChartContext({
|
| 23 |
+
get config() {
|
| 24 |
+
return config;
|
| 25 |
+
}
|
| 26 |
+
});
|
| 27 |
+
</script>
|
| 28 |
+
|
| 29 |
+
<div
|
| 30 |
+
bind:this={ref}
|
| 31 |
+
data-chart={chartId}
|
| 32 |
+
data-slot="chart"
|
| 33 |
+
class={cn(
|
| 34 |
+
'flex aspect-video justify-center overflow-visible text-xs',
|
| 35 |
+
// Overrides
|
| 36 |
+
//
|
| 37 |
+
// Stroke around dots/marks when hovering
|
| 38 |
+
'[&_.lc-highlight-point]:stroke-transparent',
|
| 39 |
+
// override the default stroke color of lines
|
| 40 |
+
'[&_.lc-line]:stroke-border/50',
|
| 41 |
+
|
| 42 |
+
// by default, layerchart shows a line intersecting the point when hovering, this hides that
|
| 43 |
+
'[&_.lc-highlight-line]:stroke-0',
|
| 44 |
+
|
| 45 |
+
// by default, when you hover a point on a stacked series chart, it will drop the opacity
|
| 46 |
+
// of the other series, this overrides that
|
| 47 |
+
'[&_.lc-area-path]:opacity-100 [&_.lc-highlight-line]:opacity-100 [&_.lc-highlight-point]:opacity-100 [&_.lc-spline-path]:opacity-100 [&_.lc-text]:text-xs [&_.lc-text-svg]:overflow-visible',
|
| 48 |
+
|
| 49 |
+
// We don't want the little tick lines between the axis labels and the chart, so we remove
|
| 50 |
+
// the stroke. The alternative is to manually disable `tickMarks` on the x/y axis of every
|
| 51 |
+
// chart.
|
| 52 |
+
'[&_.lc-axis-tick]:stroke-0',
|
| 53 |
+
|
| 54 |
+
// We don't want to display the rule on the x/y axis, as there is already going to be
|
| 55 |
+
// a grid line there and rule ends up overlapping the marks because it is rendered after
|
| 56 |
+
// the marks
|
| 57 |
+
'[&_.lc-rule-x-line:not(.lc-grid-x-rule)]:stroke-0 [&_.lc-rule-y-line:not(.lc-grid-y-rule)]:stroke-0',
|
| 58 |
+
'[&_.lc-grid-x-radial-line]:stroke-border [&_.lc-grid-x-radial-circle]:stroke-border',
|
| 59 |
+
'[&_.lc-grid-y-radial-line]:stroke-border [&_.lc-grid-y-radial-circle]:stroke-border',
|
| 60 |
+
|
| 61 |
+
// Legend adjustments
|
| 62 |
+
'[&_.lc-legend-swatch-button]:items-center [&_.lc-legend-swatch-button]:gap-1.5',
|
| 63 |
+
'[&_.lc-legend-swatch-group]:items-center [&_.lc-legend-swatch-group]:gap-4',
|
| 64 |
+
'[&_.lc-legend-swatch]:size-2.5 [&_.lc-legend-swatch]:rounded-[2px]',
|
| 65 |
+
|
| 66 |
+
// Labels
|
| 67 |
+
'[&_.lc-labels-text:not([fill])]:fill-foreground [&_text]:stroke-transparent',
|
| 68 |
+
|
| 69 |
+
// Tick labels on th x/y axes
|
| 70 |
+
'[&_.lc-axis-tick-label]:fill-muted-foreground [&_.lc-axis-tick-label]:font-normal',
|
| 71 |
+
'[&_.lc-tooltip-rects-g]:fill-transparent',
|
| 72 |
+
'[&_.lc-layout-svg-g]:fill-transparent',
|
| 73 |
+
'[&_.lc-root-container]:w-full',
|
| 74 |
+
className
|
| 75 |
+
)}
|
| 76 |
+
{...restProps}
|
| 77 |
+
>
|
| 78 |
+
<ChartStyle id={chartId} {config} />
|
| 79 |
+
{@render children?.()}
|
| 80 |
+
</div>
|
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
import { THEMES, type ChartConfig } from './chart-utils.js';
|
| 3 |
+
|
| 4 |
+
let { id, config }: { id: string; config: ChartConfig } = $props();
|
| 5 |
+
|
| 6 |
+
const colorConfig = $derived(
|
| 7 |
+
config ? Object.entries(config).filter(([, config]) => config.theme || config.color) : null
|
| 8 |
+
);
|
| 9 |
+
|
| 10 |
+
const themeContents = $derived.by(() => {
|
| 11 |
+
if (!colorConfig || !colorConfig.length) return;
|
| 12 |
+
|
| 13 |
+
const themeContents = [];
|
| 14 |
+
for (const [_theme, prefix] of Object.entries(THEMES)) {
|
| 15 |
+
let content = `${prefix} [data-chart=${id}] {\n`;
|
| 16 |
+
const color = colorConfig.map(([key, itemConfig]) => {
|
| 17 |
+
const theme = _theme as keyof typeof itemConfig.theme;
|
| 18 |
+
const color = itemConfig.theme?.[theme] || itemConfig.color;
|
| 19 |
+
return color ? `\t--color-${key}: ${color};` : null;
|
| 20 |
+
});
|
| 21 |
+
|
| 22 |
+
content += color.join('\n') + '\n}';
|
| 23 |
+
|
| 24 |
+
themeContents.push(content);
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
return themeContents.join('\n');
|
| 28 |
+
});
|
| 29 |
+
</script>
|
| 30 |
+
|
| 31 |
+
{#if themeContents}
|
| 32 |
+
{#key id}
|
| 33 |
+
<svelte:element this={'style'}>
|
| 34 |
+
{themeContents}
|
| 35 |
+
</svelte:element>
|
| 36 |
+
{/key}
|
| 37 |
+
{/if}
|
|
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<script lang="ts">
|
| 2 |
+
import { cn, type WithElementRef, type WithoutChildren } from '$lib/utils.js';
|
| 3 |
+
import type { HTMLAttributes } from 'svelte/elements';
|
| 4 |
+
import { getPayloadConfigFromPayload, useChart, type TooltipPayload } from './chart-utils.js';
|
| 5 |
+
import { getChartContext, Tooltip as TooltipPrimitive } from 'layerchart';
|
| 6 |
+
import type { Snippet } from 'svelte';
|
| 7 |
+
|
| 8 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 9 |
+
function defaultFormatter(value: any, _payload: TooltipPayload[]) {
|
| 10 |
+
return `${value}`;
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
let {
|
| 14 |
+
ref = $bindable(null),
|
| 15 |
+
class: className,
|
| 16 |
+
hideLabel = false,
|
| 17 |
+
indicator = 'dot',
|
| 18 |
+
hideIndicator = false,
|
| 19 |
+
labelKey,
|
| 20 |
+
label,
|
| 21 |
+
labelFormatter = defaultFormatter,
|
| 22 |
+
labelClassName,
|
| 23 |
+
formatter,
|
| 24 |
+
nameKey,
|
| 25 |
+
color,
|
| 26 |
+
...restProps
|
| 27 |
+
}: WithoutChildren<WithElementRef<HTMLAttributes<HTMLDivElement>>> & {
|
| 28 |
+
hideLabel?: boolean;
|
| 29 |
+
label?: string;
|
| 30 |
+
indicator?: 'line' | 'dot' | 'dashed';
|
| 31 |
+
nameKey?: string;
|
| 32 |
+
labelKey?: string;
|
| 33 |
+
hideIndicator?: boolean;
|
| 34 |
+
labelClassName?: string;
|
| 35 |
+
labelFormatter?: // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 36 |
+
((value: any, payload: TooltipPayload[]) => string | number | Snippet) | null;
|
| 37 |
+
formatter?: Snippet<
|
| 38 |
+
[
|
| 39 |
+
{
|
| 40 |
+
value: unknown;
|
| 41 |
+
name: string;
|
| 42 |
+
item: TooltipPayload;
|
| 43 |
+
index: number;
|
| 44 |
+
payload: TooltipPayload[];
|
| 45 |
+
}
|
| 46 |
+
]
|
| 47 |
+
>;
|
| 48 |
+
} = $props();
|
| 49 |
+
|
| 50 |
+
const chart = useChart();
|
| 51 |
+
const chartCtx = getChartContext();
|
| 52 |
+
|
| 53 |
+
// Filter to series with defined values (important for item-based charts like Pie/Arc
|
| 54 |
+
// where only the hovered item has a value)
|
| 55 |
+
const visibleSeries = $derived(
|
| 56 |
+
chartCtx.tooltip.series.filter((s: TooltipPayload) => s.value !== undefined)
|
| 57 |
+
);
|
| 58 |
+
|
| 59 |
+
const formattedLabel = $derived.by(() => {
|
| 60 |
+
if (hideLabel || !visibleSeries?.length) return null;
|
| 61 |
+
|
| 62 |
+
const [item] = visibleSeries;
|
| 63 |
+
const tooltipData = chartCtx.tooltip.data;
|
| 64 |
+
|
| 65 |
+
// Get the x-axis label value from the raw tooltip data (e.g. a Date or month string)
|
| 66 |
+
const dataLabel = tooltipData != null ? chartCtx.x(tooltipData) : undefined;
|
| 67 |
+
|
| 68 |
+
const key = labelKey ?? item?.label ?? item?.key ?? 'value';
|
| 69 |
+
const itemConfig = getPayloadConfigFromPayload(
|
| 70 |
+
chart.config,
|
| 71 |
+
item,
|
| 72 |
+
key,
|
| 73 |
+
tooltipData as Record<string, unknown> | null
|
| 74 |
+
);
|
| 75 |
+
|
| 76 |
+
let value: unknown;
|
| 77 |
+
if (!labelKey && typeof label === 'string') {
|
| 78 |
+
value = chart.config[label as keyof typeof chart.config]?.label ?? label;
|
| 79 |
+
} else if (labelKey) {
|
| 80 |
+
value = itemConfig?.label ?? dataLabel;
|
| 81 |
+
} else {
|
| 82 |
+
value = dataLabel;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
if (value === undefined) return null;
|
| 86 |
+
if (!labelFormatter) return value;
|
| 87 |
+
return labelFormatter(value, visibleSeries);
|
| 88 |
+
});
|
| 89 |
+
|
| 90 |
+
const nestLabel = $derived(visibleSeries.length === 1 && indicator !== 'dot');
|
| 91 |
+
</script>
|
| 92 |
+
|
| 93 |
+
{#snippet TooltipLabel()}
|
| 94 |
+
{#if formattedLabel}
|
| 95 |
+
<div class={cn('font-medium', labelClassName)}>
|
| 96 |
+
{#if typeof formattedLabel === 'function'}
|
| 97 |
+
{@render formattedLabel()}
|
| 98 |
+
{:else}
|
| 99 |
+
{formattedLabel}
|
| 100 |
+
{/if}
|
| 101 |
+
</div>
|
| 102 |
+
{/if}
|
| 103 |
+
{/snippet}
|
| 104 |
+
|
| 105 |
+
<TooltipPrimitive.Root variant="none">
|
| 106 |
+
<div
|
| 107 |
+
bind:this={ref}
|
| 108 |
+
class={cn(
|
| 109 |
+
'border-border/50 bg-background grid min-w-[9rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl',
|
| 110 |
+
className
|
| 111 |
+
)}
|
| 112 |
+
{...restProps}
|
| 113 |
+
>
|
| 114 |
+
{#if !nestLabel}
|
| 115 |
+
{@render TooltipLabel()}
|
| 116 |
+
{/if}
|
| 117 |
+
<div class="grid gap-1.5">
|
| 118 |
+
{#each visibleSeries as item, i (item.key + i)}
|
| 119 |
+
{@const key = `${nameKey || item.key || item.label || 'value'}`}
|
| 120 |
+
{@const itemConfig = getPayloadConfigFromPayload(
|
| 121 |
+
chart.config,
|
| 122 |
+
item,
|
| 123 |
+
key,
|
| 124 |
+
chartCtx.tooltip.data
|
| 125 |
+
)}
|
| 126 |
+
{@const indicatorColor = color || item.config?.color || item.color}
|
| 127 |
+
<div
|
| 128 |
+
class={cn(
|
| 129 |
+
'[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5',
|
| 130 |
+
indicator === 'dot' && 'items-center'
|
| 131 |
+
)}
|
| 132 |
+
>
|
| 133 |
+
{#if formatter && item.value !== undefined && item.label}
|
| 134 |
+
{@render formatter({
|
| 135 |
+
value: item.value,
|
| 136 |
+
name: item.label,
|
| 137 |
+
item,
|
| 138 |
+
index: i,
|
| 139 |
+
payload: visibleSeries
|
| 140 |
+
})}
|
| 141 |
+
{:else}
|
| 142 |
+
{#if itemConfig?.icon}
|
| 143 |
+
<itemConfig.icon />
|
| 144 |
+
{:else if !hideIndicator}
|
| 145 |
+
<div
|
| 146 |
+
style="--color-bg: {indicatorColor}; --color-border: {indicatorColor};"
|
| 147 |
+
class={cn('shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)', {
|
| 148 |
+
'size-2.5': indicator === 'dot',
|
| 149 |
+
'h-full w-1': indicator === 'line',
|
| 150 |
+
'w-0 border-[1.5px] border-dashed bg-transparent': indicator === 'dashed',
|
| 151 |
+
'my-0.5': nestLabel && indicator === 'dashed'
|
| 152 |
+
})}
|
| 153 |
+
></div>
|
| 154 |
+
{/if}
|
| 155 |
+
<div
|
| 156 |
+
class={cn(
|
| 157 |
+
'flex flex-1 shrink-0 justify-between leading-none',
|
| 158 |
+
nestLabel ? 'items-end' : 'items-center'
|
| 159 |
+
)}
|
| 160 |
+
>
|
| 161 |
+
<div class="grid gap-1.5">
|
| 162 |
+
{#if nestLabel}
|
| 163 |
+
{@render TooltipLabel()}
|
| 164 |
+
{/if}
|
| 165 |
+
<span class="text-muted-foreground">
|
| 166 |
+
{itemConfig?.label || item.label}
|
| 167 |
+
</span>
|
| 168 |
+
</div>
|
| 169 |
+
{#if item.value !== undefined}
|
| 170 |
+
<span class="text-foreground font-mono font-medium tabular-nums">
|
| 171 |
+
{item.value.toLocaleString()}
|
| 172 |
+
</span>
|
| 173 |
+
{/if}
|
| 174 |
+
</div>
|
| 175 |
+
{/if}
|
| 176 |
+
</div>
|
| 177 |
+
{/each}
|
| 178 |
+
</div>
|
| 179 |
+
</div>
|
| 180 |
+
</TooltipPrimitive.Root>
|
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import type { Tooltip } from 'layerchart';
|
| 2 |
+
import { getContext, setContext, type Component, type Snippet } from 'svelte';
|
| 3 |
+
|
| 4 |
+
export const THEMES = { light: '', dark: '.dark' } as const;
|
| 5 |
+
|
| 6 |
+
export type ChartConfig = {
|
| 7 |
+
[k in string]: {
|
| 8 |
+
label?: string;
|
| 9 |
+
icon?: Component;
|
| 10 |
+
} & (
|
| 11 |
+
| { color?: string; theme?: never }
|
| 12 |
+
| { color?: never; theme: Record<keyof typeof THEMES, string> }
|
| 13 |
+
);
|
| 14 |
+
};
|
| 15 |
+
|
| 16 |
+
export type ExtractSnippetParams<T> = T extends Snippet<[infer P]> ? P : never;
|
| 17 |
+
|
| 18 |
+
export type TooltipPayload = Tooltip.TooltipSeries;
|
| 19 |
+
|
| 20 |
+
// Helper to extract item config from a payload.
|
| 21 |
+
export function getPayloadConfigFromPayload(
|
| 22 |
+
config: ChartConfig,
|
| 23 |
+
payload: TooltipPayload,
|
| 24 |
+
key: string,
|
| 25 |
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
| 26 |
+
data?: Record<string, any> | null
|
| 27 |
+
) {
|
| 28 |
+
if (typeof payload !== 'object' || payload === null) return undefined;
|
| 29 |
+
|
| 30 |
+
const payloadConfig =
|
| 31 |
+
'config' in payload && typeof payload.config === 'object' && payload.config !== null
|
| 32 |
+
? payload.config
|
| 33 |
+
: undefined;
|
| 34 |
+
|
| 35 |
+
let configLabelKey: string = key;
|
| 36 |
+
|
| 37 |
+
if (payload.key === key) {
|
| 38 |
+
configLabelKey = payload.key;
|
| 39 |
+
} else if (payload.label === key) {
|
| 40 |
+
configLabelKey = payload.label;
|
| 41 |
+
} else if (key in payload && typeof payload[key as keyof typeof payload] === 'string') {
|
| 42 |
+
configLabelKey = payload[key as keyof typeof payload] as string;
|
| 43 |
+
} else if (
|
| 44 |
+
payloadConfig !== undefined &&
|
| 45 |
+
key in payloadConfig &&
|
| 46 |
+
typeof payloadConfig[key as keyof typeof payloadConfig] === 'string'
|
| 47 |
+
) {
|
| 48 |
+
configLabelKey = payloadConfig[key as keyof typeof payloadConfig] as string;
|
| 49 |
+
} else if (data != null && key in data && typeof data[key] === 'string') {
|
| 50 |
+
configLabelKey = data[key] as string;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
type ChartContextValue = {
|
| 57 |
+
config: ChartConfig;
|
| 58 |
+
};
|
| 59 |
+
|
| 60 |
+
const chartContextKey = Symbol('chart-context');
|
| 61 |
+
|
| 62 |
+
export function setChartContext(value: ChartContextValue) {
|
| 63 |
+
return setContext(chartContextKey, value);
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
export function useChart() {
|
| 67 |
+
return getContext<ChartContextValue>(chartContextKey);
|
| 68 |
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ChartContainer from './chart-container.svelte';
|
| 2 |
+
import ChartTooltip from './chart-tooltip.svelte';
|
| 3 |
+
|
| 4 |
+
export { getPayloadConfigFromPayload, type ChartConfig } from './chart-utils.js';
|
| 5 |
+
|
| 6 |
+
export { ChartContainer, ChartTooltip, ChartContainer as Container, ChartTooltip as Tooltip };
|
|
@@ -18,6 +18,7 @@
|
|
| 18 |
import SunIcon from 'phosphor-svelte/lib/SunIcon';
|
| 19 |
import MoonIcon from 'phosphor-svelte/lib/MoonIcon';
|
| 20 |
import MatchTable from '$lib/components/match-table/match-table.svelte';
|
|
|
|
| 21 |
import { buildMapRows, buildMatchRows, buildRoundRows } from '$lib/components/match-table/rows';
|
| 22 |
import { mapColumns, matchColumns, roundColumns } from '$lib/components/match-table/columns';
|
| 23 |
import type {
|
|
@@ -413,6 +414,8 @@
|
|
| 413 |
</Tabs.Root>
|
| 414 |
</section>
|
| 415 |
|
|
|
|
|
|
|
| 416 |
<!-- Citation -->
|
| 417 |
<section class="mx-auto mt-16 max-w-3xl">
|
| 418 |
<h2
|
|
|
|
| 18 |
import SunIcon from 'phosphor-svelte/lib/SunIcon';
|
| 19 |
import MoonIcon from 'phosphor-svelte/lib/MoonIcon';
|
| 20 |
import MatchTable from '$lib/components/match-table/match-table.svelte';
|
| 21 |
+
import StatsSection from '$lib/components/stats-section.svelte';
|
| 22 |
import { buildMapRows, buildMatchRows, buildRoundRows } from '$lib/components/match-table/rows';
|
| 23 |
import { mapColumns, matchColumns, roundColumns } from '$lib/components/match-table/columns';
|
| 24 |
import type {
|
|
|
|
| 414 |
</Tabs.Root>
|
| 415 |
</section>
|
| 416 |
|
| 417 |
+
<StatsSection matches={data.matches} />
|
| 418 |
+
|
| 419 |
<!-- Citation -->
|
| 420 |
<section class="mx-auto mt-16 max-w-3xl">
|
| 421 |
<h2
|