Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f596b9f50 | ||
|
|
0dbcadf0cf | ||
|
|
1e28a11007 | ||
|
|
e1394ce66c | ||
|
|
e489783022 | ||
|
|
5d330970c1 | ||
|
|
097afd085f | ||
|
|
78e3c6864b | ||
|
|
46a94b7898 | ||
|
|
72105b3a4c | ||
|
|
9764f65cf6 | ||
|
|
c565d1593b | ||
|
|
9446d7ec15 | ||
|
|
fb6684820b | ||
|
|
41b6ea08f9 | ||
|
|
388d9877b0 | ||
|
|
727a1d8e11 | ||
|
|
8e8408b2f8 | ||
|
|
14110c89e4 | ||
|
|
76db4c262d | ||
|
|
6c7a502785 | ||
|
|
e48ae26c51 | ||
|
|
ffd0db46d9 | ||
|
|
55312a19ff | ||
|
|
a97e088a52 | ||
|
|
b4d9749025 | ||
|
|
71698cd31c | ||
|
|
54aaff2f74 | ||
|
|
0d6cb316a3 | ||
|
|
5d5a39ab2a | ||
|
|
a8aa64d18b | ||
|
|
deb9db2704 | ||
|
|
9bce40819d | ||
|
|
b1a43aa7d4 | ||
|
|
155b8a1946 | ||
|
|
ef7806da03 | ||
|
|
f3b6779641 | ||
|
|
1eef943a0f | ||
|
|
cf2dba3648 | ||
|
|
52c45e53b8 | ||
|
|
224880dfc0 | ||
|
|
bf37a31802 | ||
|
|
1c583122f8 | ||
|
|
32ccc5cb39 | ||
|
|
c95590931f | ||
|
|
341f81a6a5 | ||
|
|
2f5bbca42c | ||
|
|
0cbd5a0ef2 | ||
|
|
fdcfff2554 | ||
|
|
d55897964c | ||
|
|
5850f086df | ||
|
|
2b130f052a | ||
|
|
2d0d9e3a88 | ||
|
|
488169889c | ||
|
|
f6f3427e33 | ||
|
|
cd96631b9f | ||
|
|
089fef92b1 | ||
|
|
1d2682111d | ||
|
|
c64e85e039 | ||
|
|
933f421313 | ||
|
|
c2227bffff | ||
|
|
376fa73220 | ||
|
|
aa12b6fed6 | ||
|
|
87d06fa25c | ||
|
|
45a8582aa4 | ||
|
|
b01958b1be | ||
|
|
db59284b6f | ||
|
|
61979d4aa9 | ||
|
|
4d8421529e | ||
|
|
1a16bb369b | ||
|
|
008c5a799a | ||
|
|
ef060980f7 | ||
|
|
dc772a2dab | ||
|
|
7755022227 | ||
|
|
3e263211eb | ||
|
|
20b0e09cca | ||
|
|
78a4cc8a1b | ||
|
|
32efc16ae1 | ||
|
|
33bd50081e | ||
|
|
4824657f44 | ||
|
|
50759f129c | ||
|
|
fd6ad61923 | ||
|
|
4e56f70fec | ||
|
|
f815cb3d1a | ||
|
|
ac57843c48 | ||
|
|
93096daee3 | ||
|
|
c6ea1ce032 | ||
|
|
f65e4b20af | ||
|
|
d41b16e697 | ||
|
|
5f1611998a | ||
|
|
e155cb1013 | ||
|
|
b78072d385 | ||
|
|
91977ded3d | ||
|
|
b9fb1ce5a1 | ||
|
|
4b400348e6 | ||
|
|
bc1e23255d | ||
|
|
dd075d2982 | ||
|
|
0be6adb7d2 | ||
|
|
0e0404ce0a |
@@ -1,4 +0,0 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
not ie 11
|
||||
@@ -1,5 +0,0 @@
|
||||
[*.{js,jsx,ts,tsx,vue}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
@@ -1,18 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-essential',
|
||||
'@vue/standard',
|
||||
'@vue/typescript/recommended'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020
|
||||
},
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -1,34 +0,0 @@
|
||||
# toneapi_webclient
|
||||
|
||||
Tone is an API that allows Northstar players to track their kill statistics : https://toneapi.github.io/ToneAPI_webclient/
|
||||
|
||||
If you want your server to appear on this website, you can download the server side mod from [here](https://github.com/ToneAPI/ToneAPI_servermod)
|
||||
|
||||
The server side mod will send kills data to Tone API, which will then process them.
|
||||
|
||||
You will have to set a personal token for the mod to work, please ask me for one on Discord
|
||||
|
||||
Alternatively, you can selfhost your own backend [here](https://github.com/ToneAPI/ToneAPI_backend)
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.vlist[data-v-d1645486]{overflow-y:auto}.vlist[data-v-d1645486],.vscroll[data-v-d1645486]{height:calc(100% - 3em)}.inactive[data-v-12531736],.offline[data-v-12531736]{color:var(--comment)}.serverView[data-v-12531736]{margin:0 1rem 1rem 1rem;overflow:auto}.serverList[data-v-12531736]{overflow-y:auto;height:100%}.serverHeaders[data-v-12531736],.serverRow[data-v-12531736]{box-sizing:border-box;display:grid;grid-template-columns:4ch 178px 20ch 8ch 8ch 8ch 1fr;background:var(--bg-color);text-align:left;cursor:pointer;margin:0;transition:border .25s;height:100px}.serverHeaders[data-v-12531736]{height:3em;background:var(--current-line)}.serverHeaders>span[data-v-12531736]{padding:.5em .8em 0 .25em}.serverRow.odd[data-v-12531736]{background:var(--accent)}.serverRow.uneven[data-v-12531736]{background:var(--bg-color)}.serverRow[data-v-12531736]:hover{background:var(--current-line)}.serverRow>div[data-v-12531736]{padding:.5em .8em 0 .25em}.serverRow>div[data-v-12531736]:not(:last-child){border-right:solid var(--bg-color) 2px}img[data-v-12531736]{-o-object-fit:contain;object-fit:contain;height:100%}
|
||||
@@ -0,0 +1 @@
|
||||
:root{--bg-color:#282a36;--accent:#353744;--current-line:#44475a;--comment:#6272a4;--foreground:#f8f8f2;--orange:#ffb86c;--cyan:#8be9fd;--green:#50fa7b;--pink:#ff79c6;--purple:#bd93f9;--red:#f55;--yellow:#f1fa8c}body{background:var(--bg-color);margin:0}#app{height:100vh;width:100vw;font-family:Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:var(--foreground);display:grid;grid-template-areas:"header header" "filters body " "stats body " "footer body ";grid-template-columns:1fr 5fr;grid-template-rows:auto 1fr auto auto}@media only screen and (max-width:922px){#app{grid-template-areas:"header header" "filters filters" "body body ";grid-template-rows:auto auto 1fr}}@media only screen and (max-width:600px){.github-link{display:none}}header{grid-area:header;height:64px;background:var(--bg-color);color:var(--foreground)}nav{display:flex;flex-direction:row;text-align:center;vertical-align:middle}nav a{font-weight:700;color:var(--foreground)}nav a.router-link-exact-active{color:var(--orange)}.navbar-logo img{height:48px;padding:8px 24px}.navbar-element{text-align:center;line-height:16px;padding:24px 32px}.navbar-element:hover,.navbar-logo:hover{background:var(--current-line)}.spacer{width:100%}
|
||||
@@ -0,0 +1 @@
|
||||
:root{--bg-color:#282a36;--accent:#353744;--current-line:#44475a;--comment:#6272a4;--foreground:#f8f8f2;--orange:#ffb86c;--cyan:#8be9fd;--green:#50fa7b;--pink:#ff79c6;--purple:#bd93f9;--red:#f55;--yellow:#f1fa8c}body{background:var(--bg-color);margin:0}#app{height:100vh;width:100vw;font-family:Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:var(--foreground);display:grid;grid-template-areas:"header header" "filters body " "stats body " "footer body ";grid-template-columns:1fr 5fr;grid-template-rows:auto 1fr auto auto}@media only screen and (max-width:922px){#app{grid-template-areas:"header header" "filters filters" "body body ";grid-template-rows:auto auto 1fr}}@media only screen and (max-width:600px){.github-link{display:none}}header{grid-area:header;height:64px;background:var(--bg-color);color:var(--foreground)}nav{display:flex;flex-direction:row;text-align:center;vertical-align:middle}nav a{font-weight:700;color:var(--foreground)}nav a.router-link-exact-active{color:var(--orange)}.navbar-logo img{height:48px;padding:8px 24px}.navbar-element{text-align:center;line-height:16px;padding:24px 32px}.navbar-element:hover,.navbar-logo:hover{background:var(--current-line)}.spacer{width:100%}
|
||||
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1 +0,0 @@
|
||||
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/ToneAPI_webclient/favicon.ico"><title>toneapi_webclient</title><script defer="defer" src="/ToneAPI_webclient/js/chunk-vendors.3f4f208d.js"></script><script defer="defer" src="/ToneAPI_webclient/js/app.f8f821bc.js"></script><link href="/ToneAPI_webclient/css/app.bbc51a59.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but toneapi_webclient doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1 @@
|
||||
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><meta content="Players : Tone API" property="og:title"/><meta content="Northstar players kills statistics" property="og:description"/><meta content="https://toneapi.github.io/ToneAPI_webclient/" property="og:url"/><meta content="https://toneapi.github.io/ToneAPI_webclient/og_image.png" property="og:image"/><meta name="twitter:card" content="summary_large_image"><link rel="icon" href="/ToneAPI_webclient/favicon.ico"><title>Players : Tone API</title><script defer="defer" src="/ToneAPI_webclient/js/chunk-vendors.78b0bc71.js"></script><script defer="defer" src="/ToneAPI_webclient/js/index.ecca6261.js"></script><link href="/ToneAPI_webclient/css/index.85c03c07.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but Players : Tone API doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
||||
@@ -0,0 +1 @@
|
||||
"use strict";(self["webpackChunktoneapi_webclient"]=self["webpackChunktoneapi_webclient"]||[]).push([[855],{2677:function(e,t,s){s.d(t,{Z:function(){return v}});var i=s(3396),r=s(7139);const n={ref:"vscroll",class:"vscroll"};function a(e,t,s,a,l,o){return(0,i.wg)(),(0,i.iD)("div",{ref:"vlist",class:"vlist",onScroll:t[0]||(t[0]=(...t)=>e.scroll&&e.scroll(...t))},[(0,i._)("div",n,[(0,i._)("div",{style:(0,r.j5)(`padding: ${e.interval[0]*e.$props.rowHeight}px 0px ${Math.max(0,e.list.length*e.$props.rowHeight-e.interval[1]*e.$props.rowHeight)}px;`)},[((0,i.wg)(!0),(0,i.iD)(i.HY,null,(0,i.Ko)(e.visibleList,((t,s)=>(0,i.WI)(e.$slots,"default",{data:t,index:s+e.interval[0]},void 0,!0))),256))],4)],512)],544)}var l=(0,i.aZ)({data(){return{toLoad:10,vlistHeight:10,vIndex:0}},props:{list:{type:Array,default:()=>[]},rowHeight:{type:Number,default:()=>32},highlighted:String},mounted(){this.$props.list&&(this.vlistHeight=this.$refs.vlist?.getBoundingClientRect().height||0,this.toLoad=3*this.visibleCount,this.$props.highlighted&&setTimeout((()=>this.$refs.vlist?.scrollBy({top:this.list.findIndex((e=>e.id===this.$props.highlighted))*this.rowHeight-this.$refs.vlist?.scrollTop-(this.vlistHeight/2-this.$props.rowHeight),behavior:"smooth"})),1))},computed:{interval(){return[Math.max(Math.min(this.list.length-this.toLoad,this.vIndex),0),Math.min(this.vIndex+this.toLoad,this.list.length)]},visibleList(){return this.list.slice(this.interval[0],this.interval[1])},visibleCount(){return Math.ceil(this.vlistHeight/this.$props.rowHeight)}},watch:{list(){this.vlistHeight=this.$refs.vlist?.getBoundingClientRect().height||0},highlighted(e){e&&setTimeout((()=>this.$refs.vlist?.scrollBy({top:this.list.findIndex((t=>t.id===e))*this.rowHeight-this.$refs.vlist?.scrollTop-(this.vlistHeight/2-this.$props.rowHeight),behavior:"smooth"})),1)}},methods:{scroll(e){const t=e.target?.scrollTop,s=Math.floor(t/this.$props.rowHeight),i=s<this.vIndex,r=s>this.vIndex+this.visibleCount;if(i?this.vIndex=Math.max(s-this.visibleCount,0):r&&(this.vIndex=Math.min(s,this.list.length-this.toLoad)),r||i){}}}}),o=s(89);const h=(0,o.Z)(l,[["render",a],["__scopeId","data-v-d1645486"]]);var v=h},9855:function(e,t,s){s.r(t),s.d(t,{default:function(){return w}});var i=s(3396),r=s(7139);const n=e=>((0,i.dD)("data-v-12531736"),e=e(),(0,i.Cn)(),e),a=n((()=>(0,i._)("div",{id:"filters"},null,-1))),l={class:"serverView"},o={class:"serverList"},h=n((()=>(0,i._)("div",{class:"serverHeaders"},[(0,i._)("span"),(0,i._)("span",null,"Map"),(0,i._)("span",null,"Server name"),(0,i._)("span",null,"Players"),(0,i._)("span",null,"Kills"),(0,i._)("span",null,"Max distance"),(0,i._)("span",null,"Average distance")],-1))),v=["src"],d={key:1};function u(e,t,s,n,u,p){const c=(0,i.up)("VirtualList");return(0,i.wg)(),(0,i.iD)(i.HY,null,[a,(0,i._)("div",l,[(0,i._)("div",o,[h,(0,i.Wm)(c,{list:e.serverList,"row-height":100},{default:(0,i.w5)((t=>[(0,i._)("div",{class:(0,r.C_)("serverRow "+(e.nsServersByName[t.data.name]?"":"offline ")+(e.nsServersByName[t.data.name]?.playerCount?"":"inactive ")+((t.index+1)%2?"odd ":"uneven "))},[(0,i._)("div",null,[(0,i._)("span",null,(0,r.zw)(t.index+1),1)]),e.nsServersByName[t.data.name]?((0,i.wg)(),(0,i.iD)("img",{key:0,src:`${e.publicPath}maps/${e.nsServersByName[t.data.name].map}_lobby.png`},null,8,v)):(0,i.kq)("",!0),e.nsServersByName[t.data.name]?(0,i.kq)("",!0):((0,i.wg)(),(0,i.iD)("div",d)),(0,i._)("div",null,[(0,i._)("span",null,(0,r.zw)(t.data.name+(e.nsServersByName[t.data.name]?"":" (offline)")),1)]),(0,i._)("div",null,[(0,i._)("span",null,(0,r.zw)(e.nsServersByName[t.data.name]?e.nsServersByName[t.data.name]?.playerCount+"/"+e.nsServersByName[t.data.name]?.maxPlayers:""),1)]),(0,i._)("div",null,[(0,i._)("span",null,(0,r.zw)(t.data._value.deaths),1)]),(0,i._)("div",null,[(0,i._)("span",null,(0,r.zw)(t.data._value.max_distance),1)]),(0,i._)("div",null,[(0,i._)("span",null,(0,r.zw)(Math.round(100*(t.data._value.total_distance/t.data._value.kills||0))/100),1)])],2)])),_:1},8,["list"])])])],64)}var p=s(7672),c=s(2677),m=s(4870),g=(0,i.aZ)({name:"ServerView",components:{VirtualList:c.Z},data(){return{store:(0,p.D)(),publicPath:"/ToneAPI_webclient/"}},computed:{servers(){const e=this.store.getServerList({})?.value.data;return e||(0,m.SU)(this.store.fetchServers({})).data},serverList(){const e=Object.entries(this.servers).map((e=>({...e[1],name:e[0]})));return e.sort(((e,t)=>{const s=this.nsServersByName[e.name]?this.nsServersByName[e.name].playerCount?1:0:-1,i=this.nsServersByName[t.name]?this.nsServersByName[t.name].playerCount?1:0:-1;return i-s})),e},nsServers(){const e=this.store.getNSServers;return e||this.store.fetchNSServers()},nsServersByName(){return Object.fromEntries(this.nsServers.map((e=>[e.name,e])))}}}),_=s(89);const f=(0,_.Z)(g,[["render",u],["__scopeId","data-v-12531736"]]);var w=f}}]);
|
||||
|
After Width: | Height: | Size: 308 KiB |
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 257 KiB |
|
After Width: | Height: | Size: 261 KiB |
|
After Width: | Height: | Size: 240 KiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 246 KiB |
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
After Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 253 KiB |
|
After Width: | Height: | Size: 253 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 268 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 227 KiB |
|
After Width: | Height: | Size: 1.9 MiB |
|
After Width: | Height: | Size: 245 KiB |
|
After Width: | Height: | Size: 2.3 MiB |
|
After Width: | Height: | Size: 289 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 272 KiB |
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 241 KiB |
|
After Width: | Height: | Size: 2.3 MiB |
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 2.5 MiB |
|
After Width: | Height: | Size: 287 KiB |
|
After Width: | Height: | Size: 2.2 MiB |
|
After Width: | Height: | Size: 272 KiB |
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 287 KiB |
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 280 KiB |
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"name": "toneapi_webclient",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"assert": "^2.0.0",
|
||||
"chart.js": "^4.2.1",
|
||||
"chartjs-plugin-annotation": "^2.2.1",
|
||||
"chartjs-plugin-datalabels": "^2.2.0",
|
||||
"core-js": "^3.8.3",
|
||||
"pinia": "^2.0.35",
|
||||
"vue": "^3.2.13",
|
||||
"vue-chartjs": "^5.2.0",
|
||||
"vue-class-component": "^8.0.0-0",
|
||||
"vue-multiselect": "^3.0.0-beta.1",
|
||||
"vue-router": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/assert": "^1.5.6",
|
||||
"@types/node": "^18.16.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-plugin-router": "~5.0.0",
|
||||
"@vue/cli-plugin-typescript": "~5.0.0",
|
||||
"@vue/cli-plugin-vuex": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"@vue/eslint-config-standard": "^6.1.0",
|
||||
"@vue/eslint-config-typescript": "^9.1.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-import": "^2.25.3",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"typescript": "~4.5.5"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +0,0 @@
|
||||
call npm run build
|
||||
call git add dist -f
|
||||
call git commit -m 'publish'
|
||||
call git subtree push --prefix dist origin gh-pages
|
||||
|
After Width: | Height: | Size: 512 KiB |
@@ -0,0 +1 @@
|
||||
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><meta content="Servers : Tone API" property="og:title"/><meta content="Northstar servers kills statistics" property="og:description"/><meta content="https://toneapi.github.io/ToneAPI_webclient/servers" property="og:url"/><meta content="https://toneapi.github.io/ToneAPI_webclient/server_og_image.png?" property="og:image"/><meta name="twitter:card" content="summary_large_image"><link rel="icon" href="/ToneAPI_webclient/favicon.ico"><title>Servers : Tone API</title><script defer="defer" src="/ToneAPI_webclient/js/chunk-vendors.78b0bc71.js"></script><script defer="defer" src="/ToneAPI_webclient/js/server.0510ec8b.js"></script><link href="/ToneAPI_webclient/css/server.85c03c07.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but Servers : Tone API doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|
||||
@@ -1,123 +0,0 @@
|
||||
<template>
|
||||
<header>
|
||||
<nav>
|
||||
<!-- <div class="navbar-element">hamburger</div> -->
|
||||
<div class="navbar-element"><!--<span>logo </span>--->TONE API</div>
|
||||
<div class="navbar-element github-link"><a href="https://github.com/Legonzaur/Tone_WebClient">Github</a></div>
|
||||
<div class="navbar-element github-link"><a
|
||||
href="https://github.com/Legonzaur/ToneAPI_servermod">Northstar Server Mod</a></div>
|
||||
<div class="spacer"></div>
|
||||
<!-- <router-link to="/" class="navbar-element">Home</router-link>
|
||||
<router-link to="/about" class="navbar-element">About</router-link> -->
|
||||
<router-link to="/" class="navbar-element">Players</router-link>
|
||||
</nav>
|
||||
</header>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Player, Weapon, Server, useKillStore } from '@/stores/kill'
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
setup (props, ctx) {
|
||||
const store = useKillStore()
|
||||
store.fetchPlayers({})
|
||||
store.fetchServers({})
|
||||
store.fetchWeapons({})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--bg-color: #282a36;
|
||||
--accent: #353744;
|
||||
--current-line: #44475a;
|
||||
--comment: #6272a4;
|
||||
--foreground: #f8f8f2;
|
||||
--orange: #ffb86c;
|
||||
--cyan: #8be9fd;
|
||||
--green: #50fa7b;
|
||||
--pink: #ff79c6;
|
||||
--purple: #bd93f9;
|
||||
--red: #ff5555;
|
||||
--yellow: #f1fa8c;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg-color);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
font-family: 'Helvetica', 'Arial', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: var(--foreground);
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
'header header'
|
||||
'filters body '
|
||||
'stats body '
|
||||
'footer body ';
|
||||
grid-template-columns: 1fr 5fr;
|
||||
grid-template-rows: auto 1fr auto auto;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 922px) {
|
||||
#app {
|
||||
grid-template-areas:
|
||||
'header header'
|
||||
'filters filters'
|
||||
'body body ';
|
||||
grid-template-rows: auto auto 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
.github-link{
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
grid-area: header;
|
||||
height: 64px;
|
||||
background: var(--bg-color);
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
nav a {
|
||||
font-weight: bold;
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: var(--orange);
|
||||
}
|
||||
|
||||
.navbar-element {
|
||||
text-align: center;
|
||||
line-height: 16px;
|
||||
padding: 24px 32px;
|
||||
}
|
||||
|
||||
.navbar-element:hover {
|
||||
background: var(--current-line)
|
||||
}
|
||||
|
||||
.spacer {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
Before Width: | Height: | Size: 6.7 KiB |
@@ -1,65 +0,0 @@
|
||||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br>
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-typescript" target="_blank" rel="noopener">typescript</a></li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-class-component'
|
||||
|
||||
@Options({
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
})
|
||||
export default class HelloWorld extends Vue {
|
||||
msg!: string
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
||||
@@ -1,211 +0,0 @@
|
||||
<template>
|
||||
<div class="playerChart" ref="container">
|
||||
<!-- <Scatter :data="chart" :options="chartOptions" /> -->
|
||||
<Scatter :data="chart" :options="chartOptions" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Tooltip,
|
||||
Legend,
|
||||
CoreChartOptions,
|
||||
PluginChartOptions,
|
||||
ScaleChartOptions
|
||||
} from 'chart.js'
|
||||
import annotationPlugin from 'chartjs-plugin-annotation'
|
||||
import dataLabel from 'chartjs-plugin-datalabels'
|
||||
import { useKillStore, Player, Filters } from '@/stores/kill'
|
||||
|
||||
import { Scatter } from 'vue-chartjs'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { ChartEvent } from 'chart.js/dist/core/core.plugins'
|
||||
import { _DeepPartialObject } from 'chart.js/dist/types/utils'
|
||||
|
||||
ChartJS.register(LinearScale, PointElement, LineElement, Tooltip, Legend, annotationPlugin, dataLabel)
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PlayerChart',
|
||||
props: {
|
||||
filters: Object as PropType<Filters>,
|
||||
playerHighlighted: String
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
store: useKillStore(),
|
||||
refreshColors: 0
|
||||
}
|
||||
},
|
||||
emits: ['highlightPlayer'],
|
||||
components: {
|
||||
Scatter
|
||||
},
|
||||
mounted () {
|
||||
this.refreshColors++
|
||||
},
|
||||
computed: {
|
||||
colors () {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
this.refreshColors
|
||||
const colors = {} as { [key: string]: string }
|
||||
if (this.$refs.container) {
|
||||
const style = getComputedStyle(this.$refs.container as Element)
|
||||
colors.fg = style.getPropertyValue('--foreground')
|
||||
colors.bg = style.getPropertyValue('--bg-color')
|
||||
colors.orange = style.getPropertyValue('--orange')
|
||||
colors.cyan = style.getPropertyValue('--cyan')
|
||||
colors.currentLine = style.getPropertyValue('--current-line')
|
||||
} else {
|
||||
colors.fg = '#ffffff'
|
||||
}
|
||||
return colors
|
||||
},
|
||||
chart () {
|
||||
return {
|
||||
datasets: [
|
||||
{
|
||||
label: 'Players',
|
||||
labels: this.playerIdList || [] as string[],
|
||||
borderColor: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? this.colors.orange : this.colors.cyan,
|
||||
backgroundColor: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? this.colors.orange : this.colors.cyan,
|
||||
pointRadius: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 20 : 1,
|
||||
pointStyle: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 'crossRot' : 'circle',
|
||||
hoverRadius: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.index]) ? 30 : 4,
|
||||
data: this.playerIdList.map((id) => ({ y: this.players[id].kills, x: this.players[id].deaths }))
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
chartOptions (): _DeepPartialObject<CoreChartOptions<'scatter'> & PluginChartOptions<'scatter'> & ScaleChartOptions<'scatter'>> {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
this.$props.playerHighlighted
|
||||
let endX = 0
|
||||
let endY = 0
|
||||
if (this.players) {
|
||||
const maxX = Math.max(...Object.values(this.players).map(e => e.deaths))
|
||||
const maxY = Math.max(...Object.values(this.players).map(e => e.kills))
|
||||
|
||||
if (maxY > maxX) {
|
||||
endX = maxX
|
||||
endY = (maxX / maxY) * maxY
|
||||
} else {
|
||||
endY = maxY
|
||||
endX = (maxY / maxX) * maxX
|
||||
}
|
||||
}
|
||||
return {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
animation: { duration: 500 },
|
||||
layout: { autoPadding: false },
|
||||
scales: {
|
||||
y: {
|
||||
title: {
|
||||
text: 'Kills',
|
||||
display: true,
|
||||
color: this.colors.fg
|
||||
},
|
||||
ticks: {
|
||||
color: this.colors.fg
|
||||
}
|
||||
},
|
||||
x: {
|
||||
title: {
|
||||
text: 'Deaths',
|
||||
display: true,
|
||||
color: this.colors.fg
|
||||
},
|
||||
ticks: {
|
||||
color: this.colors.fg
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (ctx: any) => {
|
||||
let label: string
|
||||
if (!this.players) {
|
||||
label = ctx.dataset.labels[ctx.dataIndex]
|
||||
} else {
|
||||
label = this.players[ctx.dataset.labels[ctx.dataIndex]].username
|
||||
}
|
||||
label += ' (' + ctx.parsed.y + ' kills ' + ctx.parsed.x + ' deaths)'
|
||||
return label
|
||||
}
|
||||
}
|
||||
},
|
||||
annotation: {
|
||||
annotations: {
|
||||
line: {
|
||||
type: 'line',
|
||||
yMin: 0,
|
||||
xMin: 0,
|
||||
yMax: endY,
|
||||
xMax: endX,
|
||||
borderWidth: 2,
|
||||
borderColor: this.colors.orange,
|
||||
borderDash: [1, 5]
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: { display: false },
|
||||
datalabels: {
|
||||
formatter: (value: any, context: any) => {
|
||||
return this.players[this.playerIdList[context.dataIndex]].username
|
||||
},
|
||||
backgroundColor: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? this.colors.orange : null,
|
||||
borderColor: this.colors.fg,
|
||||
borderWidth: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? 1 : 0,
|
||||
borderRadius: 5,
|
||||
display: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? 'true' : 'auto',
|
||||
align: -45,
|
||||
anchor: 'end',
|
||||
clamp: true,
|
||||
color: (context: any) => (this.$props.playerHighlighted && this.$props.playerHighlighted === this.playerIdList[context.dataIndex]) ? this.colors.bg : this.colors.fg
|
||||
}
|
||||
},
|
||||
onClick: (e: ChartEvent, element: any) => {
|
||||
this.$emit('highlightPlayer', element.length > 0 ? this.playerIdList[element[0].index] : undefined)
|
||||
},
|
||||
onHover: (e: any, element: any) => {
|
||||
if (!e.native.target) return
|
||||
(e.native.target as HTMLElement).style.cursor = element[0] ? 'pointer' : 'default'
|
||||
}
|
||||
}
|
||||
},
|
||||
players (): { [key: string]: Player } {
|
||||
const data = this.store.getPlayerList(this.filters || {})?.data
|
||||
if (!data) return {}
|
||||
const cut = Object.entries(data).sort((a, b) => {
|
||||
if (a[1].kills < b[1].kills) {
|
||||
return 1
|
||||
}
|
||||
if (a[1].kills > b[1].kills) {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}).slice(0, 200)
|
||||
return Object.fromEntries(cut)
|
||||
},
|
||||
playerIdList (): string[] { return Object.keys(this.players) }
|
||||
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
canvas {
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 922px) {
|
||||
canvas {
|
||||
display: none !important
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,221 +0,0 @@
|
||||
<template>
|
||||
<div class="playerTable" v-on:keydown="selectNextPlayer" tabindex="0">
|
||||
<div class="playerHeaders">
|
||||
<span></span>
|
||||
<span v-on:click="sortPlayerList('username')"
|
||||
:class="sortingData.argument == 'username' ? 'selected' : ''">Username</span>
|
||||
<span v-on:click="sortPlayerList('kills')" :class="sortingData.argument == 'kills' ? 'selected' : ''">K</span>
|
||||
<span v-on:click="sortPlayerList('deaths')" :class="sortingData.argument == 'deaths' ? 'selected' : ''">D</span>
|
||||
<span v-on:click="sortPlayerList('k/d')" :class="sortingData.argument == 'k/d' ? 'selected' : ''">K/D</span>
|
||||
<span v-on:click="sortPlayerList('max_distance')"
|
||||
:class="sortingData.argument == 'max_distance' ? 'selected' : ''">max distance</span>
|
||||
<span v-on:click="sortPlayerList('avg_distance')"
|
||||
:class="sortingData.argument == 'avg_distance' ? 'selected' : ''">average distance</span>
|
||||
</div>
|
||||
<div :class="'playerRow ' + (playerId === $props.playerHighlighted ? 'selected' : '')"
|
||||
v-for="(playerId, index) in playerIdList" v-bind:key="playerId" v-on:click="$emit('highlightPlayer', playerId)"
|
||||
:ref="`player:` + playerId">
|
||||
<div><span>{{ index+1 }}</span></div>
|
||||
<div><span>{{ players[playerId].username }}</span></div>
|
||||
<div><span>{{ players[playerId].kills }}</span></div>
|
||||
<div><span>{{ players[playerId].deaths }}</span></div>
|
||||
<div><span>{{ Math.round(players[playerId].kills / Math.max(1, players[playerId].deaths) * 100) / 100 }}</span></div>
|
||||
<div><span>{{ players[playerId].max_distance }}</span></div>
|
||||
<div><span>{{ Math.round(((players[playerId].total_distance / players[playerId].kills) || 0) * 100) / 100 }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import { useKillStore, Player, Filters } from '@/stores/kill'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
filters: { type: Object as PropType<Filters>, default: () => ({}) },
|
||||
playerHighlighted: String
|
||||
},
|
||||
emits: ['highlightPlayer'],
|
||||
data () {
|
||||
return {
|
||||
sortingData: { direction: -1, argument: 'kills' as keyof Player | 'k/d' | 'avg_distance' },
|
||||
playerIdList: [] as string[],
|
||||
store: useKillStore()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
players (): { [key: string]: Player } {
|
||||
const { player: _, ...withoutPlayer } = this.filters
|
||||
return this.store.getPlayerList(withoutPlayer)?.data || {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
players: {
|
||||
handler (newval: { [key: string]: Player }): void {
|
||||
if (!newval) return
|
||||
this.playerIdList = []
|
||||
this.sortingData.direction *= -1
|
||||
this.playerIdList = Object.keys(newval)
|
||||
this.sortPlayerList(this.sortingData.argument)
|
||||
this.playerIdList = this.playerIdList.slice(0, 500)
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
playerHighlighted (newval) {
|
||||
const newElement = this.$refs['player:' + newval] as HTMLElement[]
|
||||
if (newElement && newElement[0]) { newElement[0].scrollIntoView({ behavior: 'smooth', block: 'center' }) }
|
||||
}
|
||||
},
|
||||
updated () {
|
||||
const element = this.$refs['player:' + this.$props.playerHighlighted] as HTMLElement[]
|
||||
if (!element || !element[0]) return
|
||||
element[0].scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
},
|
||||
methods: {
|
||||
sortPlayerList (arg: ((keyof Player) | 'k/d' | 'avg_distance')) {
|
||||
if (arg === this.sortingData.argument) {
|
||||
this.sortingData.direction *= -1
|
||||
} else {
|
||||
if (arg === 'username') {
|
||||
this.sortingData.direction = 1
|
||||
}
|
||||
}
|
||||
this.sortingData.argument = arg
|
||||
|
||||
this.playerIdList.sort((a: string, b: string) => {
|
||||
if (!this.players) {
|
||||
return 0
|
||||
}
|
||||
let varA, varB
|
||||
if (arg === 'k/d') {
|
||||
varA = this.players[a].kills / Math.max(1, this.players[a].deaths)
|
||||
varB = this.players[b].kills / Math.max(1, this.players[b].deaths)
|
||||
} else if (arg === 'avg_distance') {
|
||||
varA = (this.players[a].total_distance / this.players[a].kills) || 0
|
||||
varB = (this.players[b].total_distance / this.players[b].kills) || 0
|
||||
} else {
|
||||
varA = this.players[a][arg]
|
||||
varB = this.players[b][arg]
|
||||
}
|
||||
if (varA === undefined || varB === undefined) return 0
|
||||
if (varA < varB) {
|
||||
return -1 * this.sortingData.direction
|
||||
}
|
||||
if (varA > varB) {
|
||||
return 1 * this.sortingData.direction
|
||||
}
|
||||
return 0
|
||||
})
|
||||
},
|
||||
selectNextPlayer (e: KeyboardEvent) {
|
||||
const values = {
|
||||
Home: -Infinity,
|
||||
End: Infinity,
|
||||
PageDown: 100,
|
||||
PageUp: -100,
|
||||
ArrowUp: -1,
|
||||
ArrowDown: 1,
|
||||
ArrowRight: 10,
|
||||
ArrowLeft: -10
|
||||
} as { [key: string]: number }
|
||||
if (!this.$props.playerHighlighted) return
|
||||
if (!Object.keys(values).includes(e.key)) return
|
||||
e.preventDefault()
|
||||
const index = this.playerIdList.indexOf(this.$props.playerHighlighted)
|
||||
let nextIndex = (index + values[e.key])
|
||||
if (nextIndex >= this.playerIdList.length) nextIndex = this.playerIdList.length - 1
|
||||
if (nextIndex < 0) nextIndex = 0
|
||||
this.$emit('highlightPlayer', this.playerIdList[nextIndex])
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.playerTable {
|
||||
grid-area: list;
|
||||
margin-right: 1rem;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.playerRow,
|
||||
.playerHeaders {
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
grid-template-columns: 5ch 20ch 6ch 6ch 6ch 10ch 1fr;
|
||||
background: var(--bg-color);
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
/* border: 2px solid transparent; */
|
||||
transition: border .25s;
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
.playerHeaders {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 3em;
|
||||
}
|
||||
|
||||
.playerHeaders>span {
|
||||
padding: .5em .8em 0 .25em;
|
||||
}
|
||||
|
||||
.playerRow>div {
|
||||
padding: .5em .8em 0 .25em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.playerHeaders,
|
||||
.playerRow:nth-child(2n) {
|
||||
background: var(--current-line);
|
||||
}
|
||||
|
||||
.playerRow:nth-child(2n+1) {
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
.playerHeaders div:hover,
|
||||
.playerRow:hover {
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
.playerRow>div:not(:last-child),
|
||||
.playerHeaders>div:not(:last-child) {
|
||||
border-right: solid var(--bg-color) 2px;
|
||||
}
|
||||
|
||||
.playerHeaders>span:last-child {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
|
||||
.playerRow>div:nth-child(6),
|
||||
.playerHeaders>span:nth-child(6),
|
||||
.playerRow>div:nth-child(7),
|
||||
.playerHeaders>span:nth-child(7) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.playerRow,
|
||||
.playerHeaders {
|
||||
grid-template-columns: 5ch 20ch 6ch 6ch 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.player span {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.selected {
|
||||
background: var(--comment) !important;
|
||||
}</style>
|
||||
@@ -1,176 +0,0 @@
|
||||
<template>
|
||||
<div class="weaponChart" ref="container">
|
||||
<Doughnut :data="chart" :options="chartOptions" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
|
||||
import dataLabel from 'chartjs-plugin-datalabels'
|
||||
import { Weapon, Filters, useKillStore } from '@/stores/kill'
|
||||
import { Doughnut } from 'vue-chartjs'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
|
||||
ChartJS.register(ArcElement, Tooltip, Legend, dataLabel)
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PlayerChart',
|
||||
props: {
|
||||
filters: Object as PropType<Filters>,
|
||||
playerHighlighted: String
|
||||
},
|
||||
emits: ['highlightPlayer'],
|
||||
components: {
|
||||
Doughnut
|
||||
},
|
||||
mounted () {
|
||||
this.refreshColors++
|
||||
},
|
||||
data: () => {
|
||||
return {
|
||||
store: useKillStore(),
|
||||
refreshColors: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
colors () {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
this.refreshColors
|
||||
const colors = {} as { [key: string]: string }
|
||||
if (this.$refs.container) {
|
||||
const style = getComputedStyle(this.$refs.container as Element)
|
||||
colors.fg = style.getPropertyValue('--foreground')
|
||||
colors.bg = style.getPropertyValue('--bg-color')
|
||||
colors.orange = style.getPropertyValue('--orange')
|
||||
colors.cyan = style.getPropertyValue('--cyan')
|
||||
colors.yellow = style.getPropertyValue('--yellow')
|
||||
colors.green = style.getPropertyValue('--green')
|
||||
colors.purple = style.getPropertyValue('--purple')
|
||||
colors.red = style.getPropertyValue('--red')
|
||||
colors.pink = style.getPropertyValue('--pink')
|
||||
colors.currentLine = style.getPropertyValue('--current-line')
|
||||
} else {
|
||||
colors.fg = '#ffffff'
|
||||
}
|
||||
return colors
|
||||
},
|
||||
chart () {
|
||||
const colors = [
|
||||
'cyan',
|
||||
'green',
|
||||
'orange',
|
||||
'pink',
|
||||
'purple',
|
||||
'red',
|
||||
'yellow'
|
||||
]
|
||||
const chartData = {
|
||||
datasets: [
|
||||
{
|
||||
label: 'Weapons',
|
||||
labels: this.sortedWeaponList || ([] as string[]),
|
||||
borderColor: () => this.colors.fg,
|
||||
backgroundColor: (context: any) => {
|
||||
return this.colors[
|
||||
colors[(context.dataIndex * 3) % colors.length]
|
||||
]
|
||||
},
|
||||
data: [] as number[]
|
||||
}
|
||||
]
|
||||
}
|
||||
if (!this.sortedWeaponList) {
|
||||
return chartData
|
||||
}
|
||||
chartData.datasets[0].data = this.sortedWeaponList.map((e) => {
|
||||
if (!this.weapons) {
|
||||
return 0
|
||||
}
|
||||
return this.weapons[e].kills
|
||||
})
|
||||
return chartData
|
||||
},
|
||||
chartOptions () {
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
this.$props.playerHighlighted
|
||||
const options = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: true,
|
||||
animation: { duration: 500 },
|
||||
layout: { autoPadding: false },
|
||||
plugins: {
|
||||
datalabels: {
|
||||
formatter: (value: any, context: any) => {
|
||||
return this.sortedWeaponList[context.dataIndex]
|
||||
},
|
||||
backgroundColor: (context: any) => {
|
||||
return context.dataset.backgroundColor(context)
|
||||
},
|
||||
borderColor: this.colors.fg,
|
||||
borderRadius: 25,
|
||||
borderWidth: 2,
|
||||
color: this.colors.bg,
|
||||
display: (context: any) => {
|
||||
return context.dataIndex === context.dataset.labels.length - 1
|
||||
? true
|
||||
: 'auto'
|
||||
},
|
||||
padding: 6
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: (ctx: any) => {
|
||||
let label: string
|
||||
if (!this.sortedWeaponList) {
|
||||
label = ctx.dataset.labels[ctx.dataIndex]
|
||||
} else {
|
||||
label = this.sortedWeaponList[ctx.dataIndex]
|
||||
}
|
||||
label += ' (' + this.weapons[label].kills + ' kills)'
|
||||
return label
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return options
|
||||
},
|
||||
weapons (): { [key: string]: Weapon } {
|
||||
const { weapon: _, ...withoutWeapon } = this.filters || {}
|
||||
const data = this.store.getWeaponList(withoutWeapon)?.data
|
||||
if (!data) {
|
||||
this.store.fetchWeapons(withoutWeapon)
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
},
|
||||
sortedWeaponList (): string[] {
|
||||
if (!this.weapons) return []
|
||||
const weapons = Object.keys(this.weapons)
|
||||
weapons.sort((a, b) => {
|
||||
if (Number(this.weapons[a].kills) < Number(this.weapons[b].kills)) {
|
||||
return -1
|
||||
}
|
||||
if (Number(this.weapons[a].kills) > Number(this.weapons[b].kills)) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
return weapons
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.weaponChart {
|
||||
width: calc(min(50vw, 50vh) - 3em);
|
||||
height: calc(min(50vw, 50vh) - 3em);
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 922px) {
|
||||
canvas {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,8 +0,0 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
const pinia = createPinia()
|
||||
|
||||
createApp(App).use(pinia).use(router).mount('#app')
|
||||
@@ -1,31 +0,0 @@
|
||||
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
import PlayerView from '../views/PlayerView.vue'
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: PlayerView
|
||||
}
|
||||
// {
|
||||
// path: '/players',
|
||||
// name: 'players',
|
||||
// component: PlayerView
|
||||
// },
|
||||
// {
|
||||
// path: '/about',
|
||||
// name: 'about',
|
||||
// // route level code-splitting
|
||||
// // this generates a separate chunk (about.[hash].js) for this route
|
||||
// // which is lazy-loaded when the route is visited.
|
||||
// component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
|
||||
// }
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(process.env.BASE_URL),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
||||
@@ -1,6 +0,0 @@
|
||||
/* eslint-disable */
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import * as assert from 'assert'
|
||||
|
||||
function removeNullEntries (a:any) {
|
||||
return Object.fromEntries(Object.entries(a).filter(e => e[1] !== undefined && e[1] !== null))
|
||||
}
|
||||
function objectEqual (a:any, b:any) {
|
||||
try {
|
||||
a = removeNullEntries(a)
|
||||
b = removeNullEntries(b)
|
||||
assert.deepStrictEqual(a, b)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
export interface Filters {
|
||||
server?: string;
|
||||
player?: string;
|
||||
weapon?: string;
|
||||
host?: string;
|
||||
map?: string;
|
||||
gamemode?: string;
|
||||
}
|
||||
|
||||
export interface Kill {
|
||||
deaths: number;
|
||||
kills: number;
|
||||
// eslint-disable-next-line camelcase
|
||||
max_distance: number;
|
||||
// eslint-disable-next-line camelcase
|
||||
total_distance: number;
|
||||
}
|
||||
export interface KillData<T extends Kill> {
|
||||
filter: Filters;
|
||||
data: { [key: string]: T };
|
||||
}
|
||||
|
||||
export interface Player extends Kill {
|
||||
username: string;
|
||||
// eslint-disable-next-line camelcase
|
||||
deaths_while_equipped?: number;
|
||||
}
|
||||
|
||||
export interface Weapon extends Kill {
|
||||
// eslint-disable-next-line camelcase
|
||||
deaths_while_equipped: number;
|
||||
}
|
||||
|
||||
export interface Server extends Kill {
|
||||
host: number;
|
||||
}
|
||||
|
||||
// define your typings for the store state
|
||||
export interface State {
|
||||
servers: KillData<Server>[];
|
||||
players: KillData<Player>[];
|
||||
weapons: KillData<Weapon>[];
|
||||
maps: KillData<Kill>[];
|
||||
gamemodes: KillData<Kill>[];
|
||||
hosts: { [key: number]: string };
|
||||
}
|
||||
|
||||
export const useKillStore = defineStore('kill', {
|
||||
state: (): State => ({
|
||||
servers: [],
|
||||
players: [],
|
||||
weapons: [],
|
||||
maps: [],
|
||||
gamemodes: [],
|
||||
hosts: {}
|
||||
}),
|
||||
getters: {
|
||||
getPlayerList: (state) => (filters: Filters) => state.players.find((e) => objectEqual(e.filter, filters)),
|
||||
getWeaponList: (state) => (filters: Filters) => state.weapons.find((e) => objectEqual(e.filter, filters)),
|
||||
getServerList: (state) => (filters: Filters) => state.servers.find((e) => objectEqual(e.filter, filters))
|
||||
},
|
||||
actions: {
|
||||
async fetchPlayers (filter: Filters) {
|
||||
filter = removeNullEntries(filter)
|
||||
const response = await fetch(
|
||||
'https://tone.sleepycat.date/v2_test/client/players?' +
|
||||
new URLSearchParams(filter as Record<keyof Filters, string>)
|
||||
)
|
||||
const data = await response.json()
|
||||
const entry = this.players.find((e) => objectEqual(e.filter, filter))
|
||||
if (!entry) return this.players.push({ filter, data })
|
||||
entry.data = data
|
||||
},
|
||||
async fetchWeapons (filter: Filters) {
|
||||
filter = removeNullEntries(filter)
|
||||
const response = await fetch(
|
||||
'https://tone.sleepycat.date/v2_test/client/weapons?' +
|
||||
new URLSearchParams(filter as Record<keyof Filters, string>)
|
||||
)
|
||||
const data = await response.json()
|
||||
const entry = this.weapons.find((e) => objectEqual(e.filter, filter))
|
||||
if (!entry) return this.weapons.push({ filter, data })
|
||||
entry.data = data
|
||||
},
|
||||
async fetchServers (filter: Filters) {
|
||||
filter = removeNullEntries(filter)
|
||||
const response = await fetch(
|
||||
'https://tone.sleepycat.date/v2_test/client/servers?' +
|
||||
new URLSearchParams(filter as Record<keyof Filters, string>)
|
||||
)
|
||||
const data = await response.json()
|
||||
const entry = this.servers.find((e) => objectEqual(e.filter, filter))
|
||||
if (!entry) return this.servers.push({ filter, data })
|
||||
entry.data = data
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,5 +0,0 @@
|
||||
<template>
|
||||
<div class="about">
|
||||
<h1>This is an about page</h1>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,18 +0,0 @@
|
||||
<template>
|
||||
<div class="home">
|
||||
<img alt="Vue logo" src="../assets/logo.png">
|
||||
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Options, Vue } from 'vue-class-component'
|
||||
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src
|
||||
|
||||
@Options({
|
||||
components: {
|
||||
HelloWorld
|
||||
}
|
||||
})
|
||||
export default class HomeView extends Vue {}
|
||||
</script>
|
||||
@@ -1,173 +0,0 @@
|
||||
<template>
|
||||
<div id="filters">
|
||||
<!-- <input type="number" min="0" v-model="filters.minKills">
|
||||
<input type="number" min="0" v-model="filters.minDeaths"> -->
|
||||
<!-- <select v-on:change="changeFilter({ server: ($event.target as HTMLInputElement).value })">
|
||||
<option></option>
|
||||
<option v-for="(serverData) in servers" v-bind:key="serverData.id" :value="serverData.id">{{ serverData.name }}
|
||||
</option>
|
||||
</select>
|
||||
<select v-on:change="changeFilter({ weapon: ($event.target as HTMLInputElement).value })">
|
||||
<option></option>
|
||||
<option v-for="weaponId in sortedWeaponList" v-bind:key="weaponId" :value="weaponId">{{ weaponId }}</option>
|
||||
</select> -->
|
||||
|
||||
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Select server" v-model="model.server"
|
||||
:options="groupedServers" group-values="servers" group_label="id" :group-select="false" :allow-empty="true" :multiple="false" :custom-label="((e:any) => e.name)" track-by="name" label="name"
|
||||
></VueMultiselect>
|
||||
|
||||
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Select weapon" v-model="model.weapon"
|
||||
:options="sortedWeaponList" :allow-empty="true"
|
||||
></VueMultiselect>
|
||||
|
||||
<VueMultiselect selectLabel="" deselectLabel="remove" placeholder="Search player" v-model="playerHighlighted"
|
||||
:options="sortedPlayerList" :allow-empty="true" :custom-label="((e:string) => players[e]?.username)"></VueMultiselect>
|
||||
</div>
|
||||
|
||||
<div id="playerView">
|
||||
<PlayerList :filters="filters" v-on:highlight-player="playerHighlighted = $event"
|
||||
:playerHighlighted="playerHighlighted"></PlayerList>
|
||||
<PlayerChart :filters="filters" v-on:highlight-player="playerHighlighted = $event"
|
||||
:playerHighlighted="playerHighlighted"></PlayerChart>
|
||||
<WeaponChart :filters="{player: playerHighlighted, ...filters}" :playerHighlighted="playerHighlighted"></WeaponChart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Player, Weapon, Server, useKillStore, Filters } from '@/stores/kill'
|
||||
import PlayerList from '@/components/PlayerList.vue'
|
||||
import PlayerChart from '@/components/PlayerChart.vue'
|
||||
import WeaponChart from '@/components/WeaponChart.vue'
|
||||
import { defineComponent } from 'vue'
|
||||
import VueMultiselect from 'vue-multiselect'
|
||||
import 'vue-multiselect/dist/vue-multiselect.css'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PlayerView',
|
||||
components: {
|
||||
VueMultiselect, PlayerList, PlayerChart, WeaponChart
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
model: { server: undefined, weapon: undefined } as unknown as {weapon:Weapon | Weapon[], server: (Server& {name?:string}) | (Server& {name?:string})[]},
|
||||
filters: {} as Filters,
|
||||
store: useKillStore(),
|
||||
playerHighlighted: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
servers (): { [key: string]: Server } {
|
||||
const { server: _, player: _1, ...withoutServer } = this.filters
|
||||
const data = this.store.getServerList(withoutServer)?.data
|
||||
if (!data) {
|
||||
this.store.fetchServers(withoutServer)
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
},
|
||||
groupedServers () {
|
||||
const hosts = [] as {id:string, servers:(Server& {name?:string})[] }[]
|
||||
Object.entries(this.servers).forEach((e) => {
|
||||
if (!hosts.find(host => host.id === e[1].host + 'sfdsdfsd')) hosts.push({ id: e[1].host + 'sfdsdfsd', servers: [] as Server[] })
|
||||
hosts.find(host => host.id === e[1].host + 'sfdsdfsd')?.servers.push({ name: e[0], ...e[1] })
|
||||
})
|
||||
return hosts
|
||||
},
|
||||
weapons (): { [key: string]: Weapon } {
|
||||
const { weapon: _, player: _1, ...withoutWeapons } = this.filters
|
||||
const data = this.store.getWeaponList(withoutWeapons)?.data
|
||||
if (!data) {
|
||||
this.store.fetchWeapons(withoutWeapons)
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
},
|
||||
players (): { [key: string]: Player } {
|
||||
const { player: _, ...withoutPlayers } = this.filters
|
||||
const data = this.store.getPlayerList(withoutPlayers)?.data
|
||||
if (!data) {
|
||||
this.store.fetchPlayers(withoutPlayers)
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
},
|
||||
sortedWeaponList (): string[] {
|
||||
if (!this.weapons) return []
|
||||
const weapons = Object.keys(this.weapons)
|
||||
return weapons.sort()
|
||||
},
|
||||
sortedServerList ():string[] {
|
||||
if (!this.servers) return []
|
||||
const servers = Object.keys(this.servers)
|
||||
return servers.sort()
|
||||
},
|
||||
sortedPlayerList (): string[] {
|
||||
if (!this.players) return []
|
||||
const players = Object.keys(this.players)
|
||||
return players.sort((a: string, b: string) => {
|
||||
if (!this.players) {
|
||||
return 0
|
||||
}
|
||||
if (this.players[a].username < this.players[b].username) {
|
||||
return -1
|
||||
}
|
||||
if (this.players[a].username > this.players[b].username) {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
model: {
|
||||
handler (newModel) {
|
||||
this.filters.server = newModel.server?.name
|
||||
this.filters.weapon = newModel.weapon
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.multiselect {
|
||||
margin: 0 .5em 1em .5em;
|
||||
}
|
||||
|
||||
#playerView {
|
||||
margin: 0 1rem 1rem 1rem;
|
||||
/* height:100%; */
|
||||
display: grid;
|
||||
overflow: auto;
|
||||
grid-template-areas:
|
||||
'list chart'
|
||||
'list info';
|
||||
grid-template-columns: 50% 50%;
|
||||
grid-template-rows: 50% 50%;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 922px) {
|
||||
#playerView {
|
||||
margin: 0 1rem 1rem 1rem;
|
||||
/* height:100%; */
|
||||
display: grid;
|
||||
overflow: auto;
|
||||
grid-template-areas:
|
||||
'list list';
|
||||
grid-template-columns: 100%;
|
||||
grid-template-rows: 100%;
|
||||
width: calc(100vw - 1em);
|
||||
margin: 0 0rem 1rem 1rem;
|
||||
}
|
||||
|
||||
.multiselect {
|
||||
width: calc(100% - 1em)
|
||||
}
|
||||
}
|
||||
|
||||
#filters {
|
||||
grid-area: filters;
|
||||
}
|
||||
</style>
|
||||
|
After Width: | Height: | Size: 47 KiB |
@@ -1,38 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"moduleResolution": "node",
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"useDefineForClassFields": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"webpack-env"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
const { defineConfig } = require('@vue/cli-service')
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true,
|
||||
publicPath: process.env.NODE_ENV === 'production' ? '/ToneAPI_webclient/' : '/'
|
||||
})
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 19 KiB |